PLSQL Practices

download PLSQL Practices

If you can't read please download the document

description

PLSQL Best practices

Transcript of PLSQL Practices

  • 1. Author: Duy Tran Minh Doan 10, April 2012 4/2/2014 1

2. Objective This slide provides practices which worth considered in coding PL/SQL. You should have your senior reviewed or evaluated them before using in production code. 4/2/2014 2 3. Agenda A. Practices 1. Select MIN and MAX 2. COUNT(*), COUNT(1) or COUNT(PK) ? 3. NOT IN vs MINUS 4. Ignored hints 5. Nested Loop is not good ? 6. Full table scans and Parallel hints 7. NOT IN and NOT EXISTS as outer joins 8. Correlated Subqueries Using EXISTS clause 9. Selecting many rows, unbounded result set 10. Selecting many rows, bounded result set 11. Selecting single row B. Resources C. Q/A 4/2/2014 3 4. A. Practices 1. Select MIN and MAX 2. COUNT(*), COUNT(1) or COUNT(PK) ? 3. NOT IN vs MINUS 4. Ignored hints 5. Nested Loop is not good ? 6. Full table scans and Parallel hints 7. NOT IN and NOT EXISTS as outer joins 8. Correlated Subqueries Using EXISTS clause 9. Selecting many rows, unbounded result set 10. Selecting many rows, bounded result set 11. Selecting single row 4/2/2014 A. Practices 4 5. 1. Select MIN and MAX We have table TEST with 100000 rows. SELECT COUNT(id) FROM test; Execution plan: INDEX FAST FULL SCAN| T_PK with 215 consistent gets SELECT MAX(id) FROM test; Execution plan: INDEX FULL SCAN (MIN/MAX)| T_PK with 2 consistent gets (very fast) The same with SELECT MIN(id) How about: SELECT MAX(id), MIN(id) FROM test; Execution plan: return to INDEX FAST FULL SCAN| T_PK with 215 consistent gets So, do not mix SELECT MIN and MAX together 4/2/2014A. Practices 5 6. 1. Select MIN and MAX(cont) Use this: SELECT MAX(id) FROM test UNION SELECT MIN(id) FROM test; Execution plan: 2 INDEX FULL SCAN (MIN/MAX)| T_PK with 4 consistent gets (much faster) 4/2/2014A. Practices 6 7. 2. COUNT(*), COUNT(1) or COUNT(PK) Which one is faster to get the number of row from a table? Answer: No different. Execution plan: INDEX FAST FULL SCAN| T_PK The same to count(any_string). But how about count(non_index_column)? Execution plan: TABLE ACCESS FULL (not a good idea) To make the execution plan change back to INDEX FAST FULL SCAN: SELECT COUNT (index_column) FROM Note: Although indexing make the statement run faster but still not as well as the first solution So, we should use COUNT(*), COUNT(1) or COUNT(PK) 4/2/2014A. Practices 7 8. 3. NOT IN vs MINUS Which one is faster to get rid of one part of the result? NOT IN: A full table scan is done on table1 For each table1 row, a lookup is then done in table2. If no row is found in table2, the table1 row is returned MINUS: A full scan is done on both tables The results for table2 are removed from the results for table1 4/2/2014A. Practices 8 9. 3. NOT IN vs MINUS(cont) Example: TABLE1: 20,000 rows in 1000 blocks TABLE2: 1,000 rows in 50 blocks Reads required for MINUS : Full scan of TABLE1 = 1000 blocks +Full scan of Table2 = 50 blocks = 1050 reads Reads required for NOT IN: Full scan of TABLE1 = 1000 blocks 20,000 lookups in TABLE2 = 20,000 x (depth of index on TABLE2) = 21,000 reads at least A lot more work is done by the NOT IN query. In this case, MINUS is faster. 4/2/2014A. Practices 9 10. 3. NOT IN vs MINUS(cont) Example: TABLE1: 20,000 rows in 1000 blocks TABLE2: 1,000,000 rows in 50,000 blocks Reads required for MINUS : Full scan of TABLE1 = 1000 blocks +Full scan of Table2 = 50,000 blocks = 51,000 reads Reads required for NOT IN: Full scan of TABLE1 = 1000 blocks 20,000 lookups in TABLE2 = 20,000 x (depth of index on TABLE2) = 21,000 reads at least Now, the MINUS maybe is much more heavier than NOT IN query. In this case, NOT IN is faster. 4/2/2014A. Practices 10 11. 3. NOT IN vs MINUS(cont) Conclusion: There really isnt just one right way to design queries. In some cases, you really are better off using NOT IN. In many cases, however, you should use the set operator MINUS. Once you understand the principles, you can easily choose the best method for your particular case. Note: Similar to NOT EXIST. 4/2/2014A. Practices 11 12. 4. Ignored hints Syntax error hint: Semantic error hint: Incompatible hints: Parallel with Index hints. Full with Index hints. 4/2/2014A. Practices 12 13. 5. Nested Loop is not good ? Original 4/2/2014A. Practices 13 14. 5. Nested Loop is not good ? (cont) Original execution plan 4/2/2014A. Practices 14 15. 5. Nested Loop is not good ? (cont) Enhanced 4/2/2014A. Practices 15 16. 5. Nested Loop is not good ? (cont) Enhanced - execution plan 4/2/2014A. Practices 16 17. 5. Nested Loop is not good ? (cont) Conclusion Improved response time about 33 times (7.187/0.218) for the same data set. Its depend on available data set. Enhanced version still not the best solution. 4/2/2014A. Practices 17 18. 6. Full table scans and Parallel hints We should use full table scan with parallel hint. It can take advantage of multi-CPU system. Example: SELECT /*+ FULL(OP1) PARALLEL(OP1 4) */ PROC_DATE, CLASS_SYM, EXPR_DATE, EXER_PRICE, PUT_CALL_CODE, CLEAR_FIRM_CODE, ACCT_ORIG_CODE, SUB_ACCT_CODE, SOD_LONG_QTY, SOD_SHORT_QTY, EOD_LONG_QTY, EOD_SHORT_QTY FROM OPT_DDS_OPEN_POS OP1 WHERE PROC_DATE BETWEEN v_min_date AND v_process_date 4/2/2014A. Practices 18 19. 7. NOT IN and NOT EXISTS as outer joins For faster execution plan and take advantage of hints. Example: SELECT book_key FROM book WHERE book_key NOT IN (SELECT book_key FROM sales); We can rewrite the following statement as below: SELECT b.book_key FROM book b, sales s WHERE b.book_key = s.book_key(+) and s.book_key IS NULL; 4/2/2014A. Practices 19 20. 8. Correlated Subqueries Using EXISTS clause Oracle calls this class of subqueries correlated because a Boolean condition in the where clause of the inner query references a corresponding row in the outer query. Oracle can transform the correlated subquery to a join IF: The correlated subquery must use the EXISTS clause. The outer query cannot also be a subquery (For example, a nested subquery). The correlation criteria in the inner query must use the equality operator, =. The subquery cannot contain a group by or connect by reference. The equality operator in the subquery must only return a single row. 4/2/2014A. Practices 20 21. 8. Correlated Subqueries Using EXISTS clause (cont) The following query returns all employees with bad credit: SELECT ename FROM emp e WHERE EXISTS (SELECT null FROM bad_credit b WHERE e.empno=b.empno ); We can rewrite as below: SELECT/*+ rule */ ename FROM emp e, bad_credit b WHERE e.empno=b.empno; Note: The standard join is easier to understand and also allows us to alter the execution plan by adding hints. 4/2/2014A. Practices 21 22. 9. Selecting many rows, unbounded result set 4/2/2014A. Practices 22 -- Cur is already open here. LOOP FETCH Cur INTO Record; Process_One_Record(Record); EXIT WHEN Cur%NOTFOUND; ... END LOOP; Have a look at this code 23. 9. Selecting many rows, unbounded result set (cont) 4/2/2014A. Practices 23 -- Cur is already open here. -- The fetch syntax is the same for an explicit cursor and a cursor variable. LOOP FETCH Cur BULK COLLECT INTO Results LIMIT Batchsize; -- The for loop doesn't run when Results.Count() = 0 FOR j IN 1..Results.Count() LOOP Process_One_Record(Results(j)); END LOOP; EXIT WHEN Results.Count() < Batchsize; END LOOP; CLOSE Cur; Now, we have better performance 24. 9. Selecting many rows, unbounded result set (cont) Principles Dont test the %NotFound cursor attribute to terminate the loop. Use exit when Results.Count() < Batchsize; as the last statement in the loop; When embedded SQL is sufficient, use an explicit cursor. When you need native dynamic SQL, use a cursor variable 4/2/2014A. Practices 24 25. 10. Selecting many rows, bounded result set Lets have a look: DECLARE Target_Varray_Too_Small EXCEPTION; PRAGMA Exception_Init(Target_Varray_Too_Small, -22165); BEGIN SELECT a.PK, a.v1 BULK COLLECT INTO x.Results FROM t a WHERE a.PK > x.Some_Value ORDER BY a.PK; EXCEPTION WHEN Target_Varray_Too_Small THEN Raise_Application_Error(-20000, 'Fatal: Business Rule 12345 violated.'); END; 4/2/2014A. Practices 25 26. 10. Selecting many rows, bounded result set (cont) Should be like this: -- Set the lower and upper bound of the slice. lb := Slice_Size*(Slice_No - 1) + 1; ub := lb + Slice_Size - 1; WITH Unbounded AS ( SELECT a.PK, a.v1, ROWNUM r FROM t a ORDER BY a.PK) SELECT Unbounded.PK, Unbounded.v1 BULK COLLECT INTO b.Results FROM Unbounded WHERE Unbounded.r BETWEEN b.lb AND b.ub; 4/2/2014A. Practices 26 27. 10. Selecting many rows, bounded result set (cont) Principles Fetch all the rows in a single step. Use the cursor-less PL/SQL constructs select... bulk collect into when embedded SQL is possible, and execute immediate... bulk collect into when dynamic SQL is needed. Implement an exception handler for ORA-22165 to help bug diagnosis. Fetch into a varray declared with the maximum. 4/2/2014A. Practices 27 28. 11. Selecting single row Look at this piece of code 4/2/2014A. Practices 28 Cur Sys_Refcursor; Stmt CONSTANT varchar2(200) := ' SELECT a.PK, a.v1 FROM t a WHERE a.PK = :b1'; OPEN Cur FOR Stmt USING Some_Value; -- Cur is already open here. -- The fetch syntax is the same for -- an explicit cursor and a cursor variable. FETCH Cur INTO The_Result; CLOSE Cur; 29. 11. Selecting single row(cont) We should do like this 4/2/2014A. Practices 29 DECLARE Stmt CONSTANT varchar2(200) := ' SELECT a.PK, a.v1 FROM t a WHERE a.PK = :b1'; BEGIN EXECUTE IMMEDIATE Stmt INTO The_Result USING Some_Value; SELECT a.PK, a.v1 INTO b.The_Result FROM t a WHERE a.PK = Some_Value; Embbeded SQL Native dynamic SQL 30. 11. Selecting single row(cont) Principles Fetch the row in a single step. Use the cursor-less PL/SQL constructs select... into when embedded SQL is possible. And execute immediate... into when dynamic SQL is needed. Take advantage of No_Data_Found exception and Too_Many_Rows exception. 4/2/2014A. Practices 30 31. Resources Doing SQL from PL/SQL: Best and Worst Practices Oracle. Tuning Oracle Stored Procedures Guy Harrison, Quest Software. Turbo-charge PL/SQL Performance with Bulk Processing Features Steven Feuerstein, ToadWorld.com. The Top 10 Dumbest PL/SQL Things I Have Seen or Done Steven Feuerstein, Quest Software. Oracle Documentation about Memory Architecture Understanding explain plan When NOT EXIST should NOT EXIST OCCADJ scripts. 4/2/2014B. Resources 31 32. Q/A 4/2/2014C. Q/A 32