{"id":231,"date":"2015-04-06T21:46:06","date_gmt":"2015-04-06T19:46:06","guid":{"rendered":"http:\/\/blog.sqlora.com\/de\/?p=231"},"modified":"2018-08-19T22:34:03","modified_gmt":"2018-08-19T20:34:03","slug":"merge-and-ora-30926","status":"publish","type":"post","link":"https:\/\/blog.sqlora.com\/de\/merge-and-ora-30926\/","title":{"rendered":"MERGE and ORA-30926"},"content":{"rendered":"<p>Wenn Sie h\u00e4ufiger mal ein MERGE statment verwendet haben, dann kommt Ihnen der folgende Fehler wahrscheinlich bekannt vor: ORA-30926 &#8211; unable to get a stable set of rows in the source tables. M\u00f6glicherweise haben Sie auch gemerkt, dass die Abfrage in diesem Fall l\u00e4nger dauert. Dreimal l\u00e4nger, um genauer zu sagen, denn die ganze Abfrage wird drei Mal ausgef\u00fchrt, wie die folgdenden Tests zeigen. <!--more--><\/p>\n<p>Ich habe bereits \u00fcber <a title=\"Key-preserved tables\" href=\"http:\/\/docs.oracle.com\/cd\/E11882_01\/server.112\/e25494\/views001.htm#ADMIN11783\" target=\"_blank\">key-preserved tables<\/a> und ORA-30926 bzw. ORA-01779 in meinem Beitrag zum <a title=\"BYPASS_UJVC hint\" href=\"http:\/\/blog.sqlora.com\/en\/2013\/03\/02\/bye-bye-bypass_ujvc\/\" target=\"_blank\">BYPASS_UJVC-Hint<\/a> geschrieben.<\/p>\n<p>Es gibt mehrere Gr\u00fcnde f\u00fcr diesen Fehler. Der h\u00e4ufigste ist jedoch wohl folgender.<\/p>\n<p>Laut Dokumentation ist <a href=\"http:\/\/docs.oracle.com\/cd\/E11882_01\/server.112\/e41084\/statements_9016.htm#SQLRF01606\" title=\"Merge is a deterministic statement\" target=\"_blank\">Merge ein deterministisches Statement.<\/a> Das hei\u00dft, man kann einen Datensatz nicht mehrfach mit einem MERGE aktualisieren. Wenn die Tabelle oder Abfrage in USING-Klausel bezogen auf die Spalten in der ON-Klausel Dubletten aufweist, bekommen Sie folgenden Fehler: ORA-30926 &#8211; unable to get a stable set of rows in the source tables. Wir zeigen das am folgenden Beispiel:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nCREATE TABLE t_source  (id NUMBER, val NUMBER);\r\nCREATE TABLE t_target  (id NUMBER, val NUMBER);\r\n\r\nINSERT INTO t_source VALUES  (1,10);\r\nINSERT INTO t_source VALUES  (2,10);\r\nINSERT INTO t_source VALUES  (3,10);\r\n\r\nINSERT INTO t_target VALUES  (1,1);\r\nINSERT INTO t_target VALUES  (2,1);\r\n\r\nCOMMIT;\r\n<\/pre>\n<p>Jetzt f\u00fchren wir ein MERGE-Statement aus und f\u00fchren die Datens\u00e4tze aus T_SOURCE mit den Datens\u00e4tzen aus T_TARGET zusammen. Wir erwarten erstmal keine Schwierigkeiten hier, da die Spalte ID in der T_SOURCE eindeutig ist.<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nALTER SESSION SET STATISTICS_LEVEL = ALL;\r\n\r\nCOLUMN SQL_ID NEW_VAL SQL_ID\r\nSET PAGESIZE 0\r\nSET LINESIZE 300\r\n\r\n-- First Merge without an error\r\n\r\nMERGE \/*+ gather_plan_statistics *\/\r\n\/* MERGE_TEST_1 *\/\r\nINTO t_target t\r\nUSING t_source q\r\nON (q.id = t.id)\r\nWHEN MATCHED THEN UPDATE SET  t.val = t.val + q.val\r\nWHEN NOT MATCHED THEN INSERT (id, val) VALUES(q.id, q.val);\r\n\r\nROLLBACK;\r\n\r\nSELECT sql_id from v$sql vs\r\nWHERE  VS.SQL_TEXT LIKE &#039;%MERGE_TEST_1%&#039;\r\nAND    VS.SQL_TEXT NOT LIKE &#039;%v$sql%&#039;;\r\n\r\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id=&gt;&#039;&amp;sql_id&#039;, format=&gt;&#039;IOSTATS LAST&#039;));\r\n\r\n...\r\n\r\nPlan hash value: 445881280\r\n-------------------------------------------------------------------------------------------\r\n| Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers  |\r\n-------------------------------------------------------------------------------------------\r\n|   0 | MERGE STATEMENT      |          |      1 |        |      0 |00:00:00.01 |      22  |\r\n|   1 |  MERGE               | T_TARGET |      1 |        |      0 |00:00:00.01 |      22  |\r\n|   2 |   VIEW               |          |      1 |        |      3 |00:00:00.01 |      14  |\r\n|*  3 |    HASH JOIN OUTER   |          |      1 |      3 |      3 |00:00:00.01 |      14  |\r\n|   4 |     TABLE ACCESS FULL| T_SOURCE |      1 |      3 |      3 |00:00:00.01 |       7  |\r\n|   5 |     TABLE ACCESS FULL| T_TARGET |      1 |      2 |      2 |00:00:00.01 |       7  |\r\n-------------------------------------------------------------------------------------------\r\n<\/pre>\n<p>Merken, alle Ausf\u00fchrungsschritte wurden nur einmal ausgef\u00fchrt (Starts=1). Anzahl betroffenber Datens\u00e4tze (Affected rows &#8211; A-Rows) sind auch nachvollziehbar: join von drei DAtens\u00e4tzen in T_SOURCE mit zwei aus T_TARGET.<\/p>\n<p>Nun erstellen wir eine Dublette in der T_SOURCE und wiederholen unsere \u00dcbung:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\n-- Now creating duplicate record in the source table which leads to ORA-30926\r\nINSERT INTO t_source (id, val) VALUES  (2,20);\r\n\r\nCOMMIT;\r\n\r\nMERGE \/*+ gather_plan_statistics *\/\r\n\/* MERGE_TEST_2 *\/\r\nINTO t_target t\r\nUSING t_source q\r\nON (q.id = t.id)\r\nWHEN MATCHED THEN UPDATE SET  t.val = t.val + q.val\r\nWHEN NOT MATCHED THEN INSERT (id, val) VALUES(q.id, q.val);\r\n\r\nSELECT sql_id from v$sql vs\r\nWHERE  VS.SQL_TEXT LIKE &#039;%MERGE_TEST_2%&#039;\r\nAND    VS.SQL_TEXT NOT LIKE &#039;%v$sql%&#039;;\r\n\r\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id=&gt;&#039;&amp;sql_id&#039;, format=&gt;&#039;IOSTATS LAST&#039;));\r\n\r\nPlan hash value: 445881280\r\n\r\n-------------------------------------------------------------------------------------------\r\n| Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time\t| Buffers |\r\n-------------------------------------------------------------------------------------------\r\n|   0 | MERGE STATEMENT      |          |      3 |        |      0 |00:00:00.01 |      36 |\r\n|   1 |  MERGE               | T_TARGET |      3 |        |      0 |00:00:00.01 |      36 |\r\n|   2 |   VIEW               |          |      3 |        |     10 |00:00:00.01 |      40 |\r\n|*  3 |    HASH JOIN OUTER   |\t        |      3 |      4 |     10 |00:00:00.01 |      40 |\r\n|   4 |     TABLE ACCESS FULL| T_SOURCE |      3 |      4 |     12 |00:00:00.01 |      21 |\r\n|   5 |     TABLE ACCESS FULL| T_TARGET |      3 |      2 |      6 |00:00:00.01 |      19 |\r\n-------------------------------------------------------------------------------------------\r\n\r\n<\/pre>\n<p>Was passiert da? Achten Sie darauf, wie alle Schritte nun drei Mal ausgef\u00fchrt wurden (starts=3). A-Rows f\u00fcr T_TARGET ist nun sechs (2 Datens\u00e4tze x 3 Mal) und f\u00fcr T_SOURCE ist es 12 (nun 4 Datens\u00e4tze x 3 Mal).<br \/>\nIst das ein Problem von display_cursor oder macht Oracle tats\u00e4chlich die ganze Arbeit drei Mal?<\/p>\n<p>Vielleicht k\u00f6nnen wir das pr\u00fcfen, in dem wir einen Trigger und eine Funktion anlegen:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nSET SERVEROUTPUT ON\r\n\r\nCREATE OR REPLACE TRIGGER t_merge_test\r\nBEFORE INSERT OR UPDATE ON t_target FOR EACH ROW\r\nBEGIN\r\nIF INSERTING THEN\r\n  DBMS_OUTPUT.PUT_LINE(&#039;Inserting ID=&#039;||:new.id||&#039; new val=&#039;||:new.val);\r\nELSIF UPDATING THEN\r\n  DBMS_OUTPUT.PUT_LINE(&#039;Updating ID=&#039;||:new.id||&#039; new val=&#039;||:new.val||&#039;, old val=&#039;||:old.val);\r\nEND IF;\r\nEND;\r\n\/\r\n\r\nCREATE OR REPLACE FUNCTION test_func (id NUMBER, val NUMBER) RETURN NUMBER IS\r\nBEGIN\r\n  DBMS_OUTPUT.PUT_LINE(&#039;Querying ID &#039;||id||&#039; val &#039;||val);\r\n  RETURN val;\r\nEND;\r\n\/\r\n\r\nMERGE \/*+ gather_plan_statistics *\/\r\n\/* MERGE_TEST_3 *\/\r\nINTO t_target t\r\nUSING (SELECT id, test_func(id, val) val FROM t_source) q\r\nON (q.id = t.id)\r\nWHEN MATCHED THEN UPDATE SET  t.val = t.val + q.val\r\nWHEN NOT MATCHED THEN INSERT (id, val) VALUES(q.id, q.val);\r\n\r\nQuerying ID 1 val 10\r\nUpdating ID=1 new val=11, old val=1\r\nQuerying ID 2 val 20\r\nUpdating ID=2 new val=21, old val=1\r\nQuerying ID 2 val 10\r\nUpdating ID=2 new val=11, old val=1\r\nQuerying ID 2 val 10\r\nQuerying ID 1 val 10\r\nQuerying ID 2 val 20\r\nQuerying ID 2 val 10\r\nQuerying ID 3 val 10\r\nQuerying ID 1 val 10\r\nUpdating ID=1 new val=11, old val=1\r\nQuerying ID 2 val 20\r\nUpdating ID=2 new val=21, old val=1\r\nQuerying ID 2 val 10\r\nUpdating ID=2 new val=11, old val=1\r\n\r\nSELECT DISTINCT FIRST_VALUE(sql_id) OVER (ORDER BY VS.LAST_ACTIVE_TIME DESC) sql_id\r\nFROM   v$sql vs\r\nWHERE  VS.SQL_TEXT LIKE &#039;%MERGE_TEST_3%&#039;\r\nAND    VS.SQL_TEXT NOT LIKE &#039;%v$sql%&#039;;\r\n\r\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id=&gt;&#039;&amp;sql_id&#039;, format=&gt;&#039;IOSTATS LAST&#039;));\r\n\r\n----------------------------------------------------------------------------------------------------\r\n| Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |\r\n----------------------------------------------------------------------------------------------------\r\n|   0 | MERGE STATEMENT      |          |      3 |        |      0 |00:00:00.07 |     645 |     11 |\r\n|   1 |  MERGE               | T_TARGET |      3 |        |      0 |00:00:00.07 |     645 |     11 |\r\n|   2 |   VIEW               |          |      3 |        |     10 |00:00:00.07 |     646 |     11 |\r\n|*  3 |    HASH JOIN OUTER   |          |      3 |      4 |     10 |00:00:00.01 |      40 |      0 |\r\n|   4 |     TABLE ACCESS FULL| T_SOURCE |      3 |      4 |     12 |00:00:00.01 |      21 |      0 |\r\n|   5 |     TABLE ACCESS FULL| T_TARGET |      3 |      2 |      6 |00:00:00.01 |      19 |      0 |\r\n----------------------------------------------------------------------------------------------------\r\n<\/pre>\n<p>Aus dem Output von der Funktion und Trigger kann man eindeutig erkennen: wir fragen T_SOURCE ab und aktualisieren T_TARGET, bis wir an dem Datensatz ankommen, der zum zweiten Mal aktualisiert werden soll, dann werden alle Datens\u00e4tze noch einmal abgefragt und dann fangen wir wieder an, abzufragen und zu aktualisieren, bis an dem problematischen Datensatz letztendlich der Fehler geschmissen wird. Warum passiert so was? Letztes Jahr hatte ich die M\u00f6glichkeit <a title=\"Jonathan Lewis\" href=\"https:\/\/jonathanlewis.wordpress.com\/\" target=\"_blank\">Jonathan Lewis<\/a> bei den CBO Days 2014 in Z\u00fcrich, einem Event organisiert von meinem Arbeitgeber <a title=\"Trivadis\" href=\"http:\/\/www.trivadis.com\" target=\"_blank\">Trivadis,<\/a> danach zu fragen. Nat\u00fcrlich hat mich seine Antwort auf die richtige Spur gebracht. Wir haben hier wohl mit <strong>&#8222;statement restarts&#8220;<\/strong> zu tun. In diesem Fall sind sie sogar m\u00f6glich mit nur einer Sitzung. Aber das werde ich in meinem n\u00e4chsten Beitrag beschreiben.<\/p>\n<p>\u00dcbrigens, es gibt eine Reihe von Bugs im Zusammenhang mit ORA-30926, siehe dazu MOS <a title=\"How to Troubleshoot ORA-30926 Errors? (Doc ID 471956.1)\" href=\"https:\/\/support.oracle.com\/epmos\/faces\/DocContentDisplay?id=471956.1\" target=\"_blank\">How to Troubleshoot ORA-30926 Errors? (Doc ID 471956.1)<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wenn Sie h\u00e4ufiger mal ein MERGE statment verwendet haben, dann kommt Ihnen der folgende Fehler wahrscheinlich bekannt vor: ORA-30926 &#8211; unable to get a stable set of rows in the source tables. M\u00f6glicherweise haben Sie auch gemerkt, dass die Abfrage in diesem Fall l\u00e4nger dauert. Dreimal l\u00e4nger, um genauer zu sagen, denn die ganze Abfrage [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[31,3,4,22],"tags":[35,33,10,32,34,23],"class_list":["post-231","post","type-post","status-publish","format-standard","hentry","category-merge","category-oracle","category-sql","category-trivadis","tag-dubletten","tag-duplicates","tag-merge","tag-ora-30926","tag-statement-restart","tag-trivadiscontent"],"_links":{"self":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts\/231","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/comments?post=231"}],"version-history":[{"count":10,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts\/231\/revisions"}],"predecessor-version":[{"id":442,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts\/231\/revisions\/442"}],"wp:attachment":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/media?parent=231"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/categories?post=231"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/tags?post=231"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}