{"id":791,"date":"2021-04-10T18:35:22","date_gmt":"2021-04-10T23:05:22","guid":{"rendered":"http:\/\/gregorgonzalez.com.ve\/blog\/?p=791"},"modified":"2021-04-10T18:39:31","modified_gmt":"2021-04-10T23:09:31","slug":"oracle-error-ora-04091-table-is-mutating-trigger-function-may-not-see-it","status":"publish","type":"post","link":"https:\/\/gregorgonzalez.com.ve\/blog\/oracle-error-ora-04091-table-is-mutating-trigger-function-may-not-see-it\/","title":{"rendered":"Oracle Error ORA-04091: table is mutating, trigger\/function may not see it"},"content":{"rendered":"<p>Este problema me ocurri\u00f3 cuando estaba tratando de realizar un trigger para eliminar registros cuando se actualiza o elimina un registro padre dentro de la misma tabla. Por ejemplo, teniendo una tabla \u00abclientes\u00bb que usa una relaci\u00f3n interna id_cliente y id_cliente_referido, si el id_cliente se actualizaba o eliminaba deb\u00eda cambiar\/eliminar el id del referido tambi\u00e9n.<\/p>\n<p>Este error se genera en triggers al tratar de manipular una tabla que est\u00e1 siendo modificada o va a ser modificada, limitando nuestras acciones, seg\u00fan investigu\u00e9 hay varias soluciones y varias formas de que ocurra el error. Esto puede ocurrir al hacer alguna operaci\u00f3n de lectura \u00abselect\u00bb en la misma tabla que est\u00e1 elimin\u00e1ndose o actualiz\u00e1ndose un registro al mismo tiempo, o al realizar operaciones de escritura \u00abinsert,update,delete\u00bb, tambi\u00e9n puede ocurrir si el trigger se vuelve a ejecutar causando bucle, es decir, tienes un trigger que elimina y al ejecutar un \u00abdelete\u00bb en la misma tabla, volver\u00e1 a ejecutar el trigger. Ese mismo era mi caso y Oracle lo advierte con ese error.<\/p>\n<h3>Soluciones:<\/h3>\n<h4>Usar subquery<\/h4>\n<p>Una soluci\u00f3n si lo que est\u00e1s realizando es un select, por ejemplo, select\/delete es utilizar un subquery, ya que primero ejecuta el select y luego el delete.<\/p>\n<pre class=\"lang:plsql decode:true\">DELETE FROM clientes WHERE id_cliente IN (SELECT id_cliente FROM clientes WHERE nombre = 'Juan')<\/pre>\n<p><\/br><\/p>\n<h4>Usar variables o crear tabla temporal<\/h4>\n<p>En caso de que sea m\u00e1s complejo o necesites toda la informaci\u00f3n del select para ejecutar otro query, deber\u00e1s realizar primero el select, guardar el resultado en alguna variable con la sentencia BULK COLLECT, para luego utilizarla libremente. Tambi\u00e9n se podr\u00eda crear una tabla temporal para mayor manipulaci\u00f3n.<\/p>\n<p><\/br><\/p>\n<h4>Usar procedimientos<\/h4>\n<p>Los trigger y funciones tienen ciertas limitaciones de lo que puedes realizar all\u00ed y con un procedimiento tendr\u00e1s m\u00e1s libertad, creas el trigger y luego llamas al procedimiento dentro del trigger, ejecut\u00e1ndolo como transacci\u00f3n aparte.<\/p>\n<p><\/br><\/p>\n<h4>Usar transacciones aut\u00f3nomas<\/h4>\n<pre class=\"lang:plsql decode:true\">CREATE OR REPLACE TRIGGER eliminar_referido\r\n    AFTER DELETE OR UPDATE \r\n    ON CLIENTES\r\n    FOR EACH ROW\r\nDECLARE\r\n    PRAGMA AUTONOMOUS_TRANSACTION; --Activar para transacciones autonomas\r\n    v_id_cliente NUMBER;\r\nBEGIN\r\n    v_id_cliente := :OLD.v_id_cliente;\r\n        \r\n    IF UPDATING -- Si esta actualizando\r\n    THEN \r\n        IF (:OLD.v_id_cliente != :NEW.v_id_cliente)\r\n        THEN \r\n            BEGIN\r\n                DELETE FROM clientes WHERE id_cliente_referido = v_id_cliente;\r\n                COMMIT;\r\n                DBMS_OUTPUT.PUT_LINE('CAMBIO EL CLIENTE. SE ELIMINA TODOS LOS REFERIDOS');\r\n            END;\r\n        END IF;\r\n    END IF;\r\n    \r\n    IF DELETING -- Si esta eliminando\r\n    THEN\r\n        BEGIN\r\n            DELETE FROM clientes WHERE id_cliente_referido = v_id_cliente;\r\n            COMMIT;\r\n            DBMS_OUTPUT.PUT_LINE('ELIMINO EL CLIENTE. SE ELIMINA TODOS LOS REFERIDOS');\r\n        END;\r\n    END IF;\r\nEND;<\/pre>\n<p>Yo tuve que utilizar transacciones aut\u00f3nomas para evitar el error, este tiene sus ventas y desventajas. Puedes utilizarlo como una ejecuci\u00f3n independiente en su propio bloque, lo que te da m\u00e1s libertad para manipular datos. La gran desventaja de este m\u00e9todo, es que, si es una transacci\u00f3n independiente, se ejecutar\u00e1 y har\u00e1 la eliminaci\u00f3n aunque la transacci\u00f3n padre falle y el rollback revertir\u00e1 solo la padre. Hay que tener cuidado con esto y siempre probar y probar para evitar futuros errores.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Este problema me ocurri\u00f3 cuando estaba tratando de realizar un trigger para eliminar registros cuando se actualiza o elimina un registro padre dentro de la misma tabla. Por ejemplo, teniendo una tabla \u00abclientes\u00bb que usa una relaci\u00f3n interna id_cliente y id_cliente_referido, si el id_cliente se actualizaba o eliminaba deb\u00eda cambiar\/eliminar el id del referido tambi\u00e9n. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":660,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[239],"tags":[178,294,295,293],"_links":{"self":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/791"}],"collection":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/comments?post=791"}],"version-history":[{"count":8,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/791\/revisions"}],"predecessor-version":[{"id":799,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/791\/revisions\/799"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/media\/660"}],"wp:attachment":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/media?parent=791"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/categories?post=791"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/tags?post=791"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}