MERGE SQL Statement: Lesser Known Facets

MERGE SQL Statement: Lesser Known Facets

MERGE SQL Statement: Lesser Known Facets Andrej Pashchenko @Andrej_SQL blog.sqlora.com About me • Working at Trivadis Germany, Düsseldorf • Focusing on Oracle: • Data Warehousing • Application Development • Application Performance • Course instructor „Oracle New Features for Developers“ @Andrej_SQL blog.sqlora.com • MERGE is a part of SQL 2003 and has been introduced in Oracle 9i • Since then, the MERGE has been well adopted and widely used, but sometimes still has some confusing or unexpected behavior ORA-30926 ORA-30926 • ORA-30926 is definitely the most confusing error related to MERGE • The error description is somewhat confusing too: • One of the reasons is clearly documented: ORA-30926 - Example • Create an „overlaping“ bonus list and try to merge it in employee table INSERT INTO scott.bonus (ename, job, sal, comm) SELECT ename, job, sal, sal*0.2 comm Sales department 20% FROM scott.emp WHERE deptno = 30; INSERT INTO scott.bonus (ename, job, sal, comm) SELECT ename, job, sal, sal*0.1 comm Each manager 10% FROM scott.emp WHERE job = 'MANAGER'; SQL> MERGE INTO scott.emp e 2 USING scott.bonus b But SALES also has a manager: 3 ON (b.ename = e.ename) BLAKE will be updated twice 4 WHEN MATCHED THEN UPDATE set e.comm = b.comm; USING scott.bonus b * ERROR at line 2: ORA-30926: unable to get a stable set of rows in the source tables ORA-30926 - How to find the problem? • Check the duplicates in the source with respect to the ON-keys: SQL> MERGE INTO scott.emp e SQL> SELECT ename 2 USING scott.bonus b 2 FROM scott.bonus b 3 ON (b.ename = e.ename) 3 GROUP BY ename 4 WHEN MATCHED THEN 4 HAVING COUNT(*) > 1; 5 UPDATE SET e.comm = b.comm; USING scott.bonus b ENAME * ---------- ERROR at line 2: BLAKE ORA-30926: unable to get a stable set of rows in the source tables • Find the correct way to avoid duplicates. Often this is a business question, e.g. use MAX or SUM: SQL>SELECT ename, MAX(comm) comm FROM scott.bonus b GROUP BY ename; ORA-30926 Fixing the problem • Fix the problem in the source data or directly in your query: SQL> MERGE INTO scott.emp e 2 USING (SELECT ename, MAX(comm) comm 3 FROM scott.bonus b 4 GROUP BY ename) b 5 ON (b.ename = e.ename) 6 WHEN MATCHED THEN UPDATE set e.comm = b.comm; 8 rows merged. • What does the documentation say about ORA-30926: ORA-30926: unable to get a stable set of rows in the source tables Cause: A stable set of rows could not be got because of large dml activity or a non- deterministic where clause. Action: Remove any non-deterministic where clauses and reissue the dml. The whole execution three times? • Have you noticed that the execution takes much longer if you get ORA-30926? SQL> MERGE INTO scott.emp e 2 USING scott.bonus b 3 ON (b.ename = e.ename) 4 WHEN MATCHED THEN UPDATE SET e.comm = b.comm; USING scott.bonus b * ERROR at line 2: ORA-30926: unable to get a stable set of rows in the source tables ----------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | ----------------------------------------------------------------- | 0 | MERGE STATEMENT | | 3 | | 0 | | 1 | MERGE | EMP | 3 | | 0 | | 2 | VIEW | | 3 | | 21 | |* 3 | HASH JOIN | | 3 | 9 | 21 | | 4 | TABLE ACCESS FULL| BONUS | 3 | 9 | 27 | | 5 | TABLE ACCESS FULL| EMP | 3 | 14 | 26 | ----------------------------------------------------------------- Write Consistency and DML restarts Write Consistency and DML Restarts (UPDATE) SQL> SELECT ename, sal, comm ENAME SAL COMM FROM emp WHERE sal < 1000; ---------- ---------- ---------- SMITH 800 JAMES 950 Session 1 Session 2 SQL> UPDATE emp Cannot update JAMES‘s row an waits t1 2 SET sal = 1000 3 WHERE ename = 'JAMES'; SQL> UPDATE emp t 2 SET comm = nvl(comm,0) + 1000 2 3 WHERE sal < 1000; COMMIT; t3 Session 2 can now update JAMES, but JAMES‘s salary is not less than 1000 anymore t4 ENAME SAL COMM ---------- ---------- ---------- SMITH 800 1000 JAMES 1000 Write Consistency and DML Restart Identify rows to be updated Get row in Get SCN1 in consistent mode (per SCN1) current mode No Request new Rollback all changes tracked columns SCN made so far unchanged? Identify rows to be updated Yes Up to 5000 in consistent mode (new SCN) times Update the row SELECT FOR UPDATE (current mode) Tracked: columns in No Yes WHERE and :OLD, :NEW tracked columns Update locked rows unchanged? values in BEFORE EACH ROW trigger Write Consistency and DML Restarts (MERGE) Session 1 Session 2 SQL> UPDATE emp Waits for locked row 2 SET sal = 1000 t1 3 WHERE ename = 'JAMES'; SQL> MERGE INTO emp t 2 USING (SELECT 1000 comm FROM dual) q t 3 ON (t.sal < 1000) 2 4 WHEN MATCHED THEN UPDATE 5 SET t.comm = nvl(t.comm,0)+q.comm; COMMIT; t3 Session 2 can now update JAMES, but JAMES‘s salary is not less than 1000 anymore t4 ENAME SAL COMM ---------- ---------- ---------- SMITH 800 1000 JAMES 1000 1000 Write Consistency and DML Restarts (MERGE) Session 1 Session 2 SQL> UPDATE emp Waits for locked row 2 SET sal = 1000 t1 3 WHERE ename = 'JAMES'; SQL> MERGE INTO emp t 2 USING (SELECT 1000 comm FROM dual) q t 3 ON (t.sal < 1000) 2 4 WHEN MATCHED THEN UPDATE 5 SET t.comm = nvl(t.comm,0)+q.comm 6 WHERE (t.sal < 1000) ; t3 COMMIT; ENAME SAL COMM t4 ---------- ---------- ---------- SMITH 800 1000 JAMES 1000 Write Consistency and DML Restarts (MERGE) Session 1 Session 2 SQL> UPDATE emp Waits for locked row 2 SET comm = 500 t1 3 WHERE ename = 'JAMES'; SQL> MERGE INTO emp t 2 USING (SELECT 1000 comm FROM dual) q t 3 ON (t.sal < 1000) 2 4 WHEN MATCHED THEN UPDATE 5 SET t.comm = nvl(t.comm,0)+ q.comm; t3 COMMIT; ENAME SAL COMM t4 ---------- ---------- ---------- SMITH 800 1000 JAMES 950 1500 Write Consistency and DML Restart • MERGE can show a different behavior regarding DML restarts • There was a bug until 18c about not tracking ON-columns, but it is still there even in 19c in some cases • But MERGE is tracking columns in SET clause thus preventing “lost updates” during running statements (no replace for locking strategy in your app) • In case of DML restart triggers can fire multiple times, so avoid any non- transactional logic or autonomous transactions in triggers! ORA-30926 revisited Write Consistency and DML Restart • Obviously Oracle is using the same mechanism of mini rollbacks as with write consistency also to ensure its deterministic behavior • SET-columns are tracked • Even within the same session updating the column to another value will be detected • The MERGE statement will be restarted • As a result, we can observe the mentioned triple effort: MERGE and than rollback, SELECT FOR UPDATE and then MERGE again ORA-38104 ORA-38104 • Oracle doesn’t allow to update columns used in ON clause • If you feel like you have to do this, verify your requirements carefully • Sometimes useful for ad hoc data manipulation, fixing erroneous data, etc. EMP • Assign the role EMPNO ENAME ACCOUNTING to each EMP_ROLES employee of deptno=10 7839 KING ENAME ROLE_NAME 7782 CLARK • If an employee is MERGE? KING ACCOUNTING assigned the DEFAULT 7934 MILLER CLARK SALES role, overwrite this assignment. EMP_ROLES CLARK ACCOUNTING ENAME ROLE_NAME MILLER ACCOUNTING KING DEFAULT CLARK SALES ORA-38104 • The straightforward approach doesn’t work: SQL> MERGE INTO emp_roles t 2 USING (SELECT empno, 'ACCOUNTING' role_name, 'DEFAULT' old_role 3 FROM emp 4 WHERE deptno = 10 5 ) q 6 ON (t.empno = q.empno and t.role_name = q.old_role) 7 WHEN MATCHED THEN UPDATE SET t.role_name = q.role_name 8 WHEN NOT MATCHED THEN INSERT VALUES (q.empno, q.role_name) ; ON (t.empno = q.empno and t.role_name = q.old_role) * ERROR at line 6: ORA-38104: Columns referenced in the ON Clause cannot be updated: "T"."ROLE_NAME" ORA-38104 • Using WHERE clause instead of ON only seems to work, because the result is wrong: no new role assignment for MILLER (7782) SQL> MERGE INTO emp_roles t 2 USING (SELECT empno, 'ACCOUNTING' role_name EMP_ROLES 3 FROM emp 4 WHERE deptno =10 ENAME ROLE_NAME 5 ) q KING ACCOUNTING 6 ON (t.empno = q.empno) 7 WHEN MATCHED THEN UPDATE CLARK SALES 8 SET role_name = q.role_name 9 WHERE t.role_name = 'DEFAULT' MILLER ACCOUNTING 10 WHEN NOT MATCHED THEN INSERT (empno, role_name) 11 VALUES (q.empno, q.role_name) ; 2 rows merged. ORA-38104 – Using ROWID • Doing the whole logic in USING subquery and merging on ROWID • At the price of increased complexity and performance penalty of one more join SQL> MERGE INTO emp_roles t 2 USING (SELECT r.rowid rid, 'ACCOUNTING' new_role_name 3 , e.empno EMP_ROLES 4 FROM emp e LEFT JOIN emp_roles r 5 ON e.empno = r.empno ENAME ROLE_NAME 6 AND r.role_name = 'DEFAULT' KING ACCOUNTING 7 WHERE e.deptno = 10 8 ) q CLARK SALES 9 ON (t.rowid = q.rid ) 10 WHEN MATCHED THEN UPDATE CLARK ACCOUNTING 11 SET role_name = q.new_role_name 12 WHEN NOT MATCHED THEN INSERT (empno, role_name) MILLER ACCOUNTING 13 VALUES (q.empno, q.new_role_name) ; 3 rows merged. ORA-38104 – Fooling the Parser • Using a subquery SQL> MERGE INTO emp_roles t 2 USING (SELECT empno, 'ACCOUNTING' role_name 3 , 'DEFAULT' old_role EMP_ROLES 4 FROM emp 5 WHERE deptno = 10 ENAME ROLE_NAME 6 ) q KING ACCOUNTING 7 ON ( t.empno = q.empno 8 AND (SELECT t.role_name FROM dual) = q.old_role ) CLARK SALES 9 WHEN MATCHED THEN UPDATE SET role_name = q.role_name 10 WHEN NOT MATCHED THEN INSERT (empno, role_name) CLARK ACCOUNTING 11 VALUES (q.empno, q.role_name) ; MILLER ACCOUNTING 3 rows merged.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    35 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us