Presentation is loading. Please wait.

Presentation is loading. Please wait.

SQL Tuning 101 excerpt: Explain Plan A Logical Approach Michael Ruckdaschel Affinion Group International.

Similar presentations


Presentation on theme: "SQL Tuning 101 excerpt: Explain Plan A Logical Approach Michael Ruckdaschel Affinion Group International."— Presentation transcript:

1 SQL Tuning 101 excerpt: Explain Plan A Logical Approach Bymruckdaschel@affiniongroup.com Michael Ruckdaschel Affinion Group International

2 My Qualifications Software Developer for Affinion Group International for 12 years an expert in SQL Performance Tuning for Oracle Databases. Over 8 years tuning experience Over 20 years Applications Design and Development experience. Current tuning experience includes Oracle 9iR2 RAC, and earlier versions of Oracle to Version 7, with databases up to 7 Terabytes in size

3 Question: What is the Single Most-Effective way to tune a SQL statement? Answer: Reduce the amount of work Oracle needs to do to return the desired result. That’s All You Need To Know!!!!

4 ????? My SQL is performing poorly, there must be other users / processes slowing me down. If only there was an index on column “x”, my SQL would perform ????? It doesn’t matter how I write my SQL or Process, we can always tune it later. MYTH: It doesn’t matter how I write my SQL or Process, we can always tune it later. FACT: SQL Tuning cannot overcome poor process design. MYTH: I need to add hints to every query to make Oracle perform. Oracle is going to choose the wrong access method most of the time. MYTH: My SQL is performing poorly, there must be other users / processes slowing me down. If only there was an index on column “x”, my SQL would perform MYTH: Full Table Scans are bad! Index Access is always better! FACT: There are many times where a Full Table Scan is going to be more efficient than Index Access, and vice-versa. FACT: Given correct information (i.e. Statistics), and properly written SQL, Oracle will make the “right” decision in its execution of a Given Query most of the time. FACT: 90% of all performance problems involve inefficiency of your SQL and process, NOT problems with the database (i.e. indexing, other users, file system, data model, etc) ????? Solely from looking at the Explained Plan one can tell how a query will perform. FACT: Only executing a query will determine its performance characteristics. However, comparing two plans, one may estimate which will likely incur “less work”. ????? I need to add hints to every query to make Oracle perform. Oracle is going to choose the wrong access method most of the time. ????? Full Table Scans are bad! Index Access is always better! MYTH: Solely from looking at the Explained Plan one can tell how a query will perform. Myth or Fact???

5 Our Agenda What is Oracle going to do? How does Oracle derive this plan? How do we find out Oracle’ plan? How do we read an Explain Plan? –Access Methods –Join Methods and Order –Stepping through the plan Plan Evaluation

6 Given a SQL Statement, What is Oracle going to do? The steps Oracle is going to go through in executing our query, to get us the information we’ve requested. FACT:Oracle only knows how to deal with 1-2 row sources (e.g. Tables) at a time. Explain Plan: Possible Exception is BITMAP table access --More on that later.

7 E XPLAIN P LAN(Cont’d) It will Tell Us: … what Table Access Method Oracle is going to use to access each table in our query (Direct Row Access or Table/Index Scan) … what Join Method Oracle is going use to join each row set with another row set. … what order Oracle is going to access and join the tables in our query. … how much data (rows and bytes) Oracle is estimating the query is going to access at each step. …, if the underlying table is partitioned, how many, and which, partitions will be scanned / accessed.

8 SELECT … FROM temp_open_ar_transactions ar,fin_trans ft,ftc_inv fi,member_contract mc,recognition_fin_link rfl,member_contract_attrib mca,refund_method rm,member_recognition mr,partner p,partner_corporation pc WHERE ar.fin_trans_sysid = ft.fin_trans_sysid AND ft.ftc_inv_sysid = fi.ftc_inv_sysid AND ft.member_contract_sysid = mc.member_contract_sysid AND ft.cur_recognition_fin_link_sysid = rfl.recognition_fin_link_sysid AND rfl.mem_contract_attrib_sysid = mca.member_contract_attrib_sysid AND mca.refund_method_id = rm.refund_method_id AND mca.partner_id = p.partner_sysid AND p.partner_corporation_sysid = pc.partner_corporation_sysid AND rfl.recognition_fin_link_sysid = mr.recognition_fin_link_sysid AND mr.recognition_date <= cycle_date.mth_cycle_date_eod AND mr.closed_date IS NULL GROUP BY … HAVING mr_revenue_amt != 0 AND mr_comm_amt != 0 What do you think of this SQL?

9 ------------------------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes |TempSpc| Cost | Pstart| Pstop | ------------------------------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 9872 | 2371K| | 2029K| | | |* 1 | FILTER | | | | | | | | | 2 | SORT GROUP BY | | 9872 | 2371K| 981M| 2029K| | | |* 3 | HASH JOIN | | 3948K| 926M| | 1992K| | | | 4 | TABLE ACCESS FULL | REFUND_METHOD | 28 | 196 | | 2 | | | |* 5 | HASH JOIN | | 3948K| 899M| | 1992K| | | | 6 | TABLE ACCESS FULL | PARTNER_CORPORATION | 1323 | 26460 | | 2 | | | |* 7 | HASH JOIN | | 3948K| 824M| | 1992K| | | | 8 | TABLE ACCESS FULL | PARTNER | 3896 | 35064 | | 3 | | | |* 9 | HASH JOIN | | 3948K| 790M| 400M| 1991K| | | | 10 | TABLE ACCESS FULL | MEMBER_CONTRACT_ATTRIB | 8230K| 306M| | 3809 | | | |* 11 | HASH JOIN | | 3948K| 643M| 15M| 1973K| | | | 12 | TABLE ACCESS FULL | FTC_INV | 618K| 9062K| | 80 | | | |* 13 | HASH JOIN | | 3948K| 587M| 576M| 1965K| | | |* 14 | HASH JOIN | | 3948K| 530M| 1998M| 1867K| | | |* 15 | HASH JOIN | | 17M| 1797M| 1746M| 1072K| | | |* 16 | HASH JOIN | | 17M| 1545M| 1226M| 613K| | | | 17 | TABLE ACCESS FULL | TEMP_OPEN_AR_TRANSACTIONS | 17M| 1024M| | 4856 | | | | 18 | TABLE ACCESS FULL | FIN_TRANS | 552M| 15G| | 229K| | | | 19 | PARTITION RANGE ALL | | | | | | 1 | 22 | | 20 | TABLE ACCESS FULL | RECOGNITION_FIN_LINK | 608M| 8708M| | 120K| 1 | 22 | | 21 | PARTITION RANGE ITERATOR| | | | | | 1 | KEY | |* 22 | TABLE ACCESS FULL | MEMBER_RECOGNITION | 136M| 4425M| | 637K| 1 | KEY | | 23 | TABLE ACCESS FULL | MEMBER_CONTRACT | 144M| 2073M| | 42574 | | | ------------------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(ROUND(SUM("MR"."REVENUE_AMT"*"AR"."UNAPP_PERCENT"),2)<>0 AND ROUND(SUM("MR"."COMM_AMT"*"AR"."UNAPP_PERCENT"),2)<>0) 3 - access("MCA"."REFUND_METHOD_ID"="RM"."REFUND_METHOD_ID") 5 - access("P"."PARTNER_CORPORATION_SYSID"="PC"."PARTNER_CORPORATION_SYSID") 7 - access("MCA"."PARTNER_ID"="P"."PARTNER_SYSID") 9 - access("RFL"."MEM_CONTRACT_ATTRIB_SYSID"="MCA"."MEMBER_CONTRACT_ATTRIB_SYSID") 11 - access("FT"."FTC_INV_SYSID"="FI"."FTC_INV_SYSID") 13 - access("FT"."MEMBER_CONTRACT_SYSID"="MC"."MEMBER_CONTRACT_SYSID") 14 - access("RFL"."RECOGNITION_FIN_LINK_SYSID"="MR"."RECOGNITION_FIN_LINK_SYSID") 15 - access("FT"."CUR_RECOGNITION_FIN_LINK_SYSID"="RFL"."RECOGNITION_FIN_LINK_SYSID") 16 - access("AR"."FIN_TRANS_SYSID"="FT"."FIN_TRANS_SYSID") 22 - filter("MR"."CLOSED_DATE" IS NULL AND "MR"."RECOGNITION_DATE"<="INFOCTL"."CYCLE_DATE"."MTH_CYCLE_DATE_EOD"())

10 How does Oracle derive this plan? The Cost-Based Optimizer The optimizer determines the most efficient way to execute a SQL statement after considering many factors related to the objects referenced and the conditions specified in the query. This determination is an important step in the processing of any SQL statement and can greatly affect execution time 1 It is a program that uses various database statistics, and other “knowledge” of the data, to derive estimated costs for “all” of the various ways a given join condition or data selection may be performed. It selects the “lowest cost” method of achieving the desired result. Most of the time this works very well.

11 The Cost-Based Optimizer (Cont’d) But sometimes, Oracle does not “know” the data as well as we do. Oracle will occasionally start with the wrong table Or, Oracle will occasionally choose the wrong Join or Access method, etc. for one or more tables In these instances, we “tune” the SQL (e.g. Manually alter the SQL for execution path). What are some ways we can do this? Hints –Change Join Order –Change Join Method (Hash Join, Sort Join, Nested Loops, etc…) –Force Index Usage Rewrite the SQL –Prevent Index Access (Apply function to indexed column) –Use correlated sub-query vs. table join –Create an in-line view in the FROM clause

12 The Cost-Based Optimizer (Cont’d) Some functions of the CBO: Evaluation of expressions and conditions Statement Transformation Choose Optimization Approach (Best Throughput, or Response Time) Choose Access Paths Choose Join Orders Choose Join Method Statistics are CRUCIAL to this processing!!!

13 Figure 1-2 Cost-Based Optimizer Components

14 Statistics generated include the following: Table statistics –Number of rows –Number of blocks –Average row length Column statistics –Number of distinct values (NDV) in column –Number of nulls in column –Data distribution (histogram) Index statistics –Number of leaf blocks –Levels –Clustering factor System statistics –I/O performance and utilization –CPU performance and utilization

15 Without statistics Oracle makes bad decisions! w/o Stats Oracle Assumes (for tables): Table StatisticDefault Number of rows num_of_blocks * (block_size - cache_layer) / avg_row_len Average row length100 Bytes Number of blocks100 blocks Remote – Number of rows2000 rows Remote – Average row length100 bytes

16 Without statistics Oracle makes bad decisions! w/o Stats Oracle Assumes (for indexes): Index StatisticDefault Levels1 Leaf blocks25 Leaf blocks per key1 Data blocks per key1 Distinct keys100 Clustering Factor800 (8 * number of blocks)

17 Bad Stats Example FIN_TRANS Table OWNER TABLE NAME INDEX_NAME DISTINCT_KEYS ---------- ----------- ------------------------------ ------------- FDS FIN_TRANS FIN_TRANS_MEMCONSYSID 195840 TABLE_NAME NUM_ROWS BLOCKS ------------------------------ ---------- ---------- FIN_TRANS 577445040 3939004 TABLE_NAME NUM_ROWS BLOCKS ------------------------------ ---------- ---------- TMP_NFT_NEW 719 10 195,840 Member Contract Sysids in FIN_TRANS ? 577M / 195,840 = 2949 Rows per key (MC_SYSID)

18 SELECT * FROM fin_trans ft,tmp_nft_new tnn WHERE tnn.member_contract_sysid = ft.member_contract_sysid Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=284543Cardinality=13349Bytes=3177062) 1 0 HASH JOIN (Cost=284543Card=13349Bytes=3177062) 2 1 TABLE ACCESS (FULL) OF 'TMP_NFT_NEW’ (Cost=2Cardinality=719Bytes=97065) 3 1 TABLE ACCESS (FULL) OF 'FIN_TRANS’ (Cost=240055Cardinality=577445040Bytes=59476839120) Bad Stats Example

19 SET AUTOTRACE TRACEONLY EXPLAIN 1 select count(1) 2 from member_contract mc 3,member_contract_attrib mca 4* where mca.member_contract_attrib_sysid = cur_mem_contract_attrib_sysid / Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=23585 Card=1 Bytes=12) 1 0 SORT (AGGREGATE) 2 1 NESTED LOOPS (Cost=23585 Card=153843400 Bytes=1846120800) 3 2 INDEX (FAST FULL SCAN) OF 'MC_MCAS_IDX' (NON-UNIQUE) (Cost=13745 Card=153843400 Bytes=923060400) 4 2 INDEX (UNIQUE SCAN) OF 'PK_MEMBER_CONTRACT_ATTRIB' (UNIQUE) (Cost=1 Card=1 Bytes=6) How do we find out Oracle’ Plan? Using SQL*Plus Autotrace Facility

20 Or: Using the Explain Plan Stmt. 1 EXPLAIN PLAN 2 SET statement_id = 'MRUCKDASCHEL' FOR 3 SELECT count(1) 4 FROM member_contract mc 5,member_recognition mr 6 WHERE mr.member_contract_sysid = mc.member_contract_sysid 7 AND mr.recognition_date > '01-jan-2006' #> / Explained. Explain Plan Set Statement Id = ‘ ’ For Such As:

21 This will store rows in a table called: PLAN_TABLE keyed by STATEMENT_ID These are retrieve using SQL like: SELECT decode(id, 0,'1.1', lpad(' ', 2 * (level - 1)) || level || '.' || position) || ' ' || initcap(decode(object_type, null, null, object_type || ' ')) ||initcap(operation) || ' ' || decode(object_name, null, initcap(options),'(' || initcap(options) || ') of ' || object_name) || ' ' ||decode(id, 0, decode(position, null, 'Rule', 'Cost = ' || position)) Query_Plan FROM plan_table CONNECT BY PRIOR id = parent_id AND statement_id = USER START WITH id = 0 AND statement_id = USER /

22 This will produce a plan like: QUERY_PLAN ------------------------------------------------- 1.1 Select Statement Cost = 1374725 2.1 Sort Aggregate 3.1 Hash Join 4.1 Unique Index (Fast Full Scan) of PK_MEMBER_CONTRACT_MEMCONSYSID 4.2 Partition Range Iterator 5.1 Table Access (Full) of MEMBER_RECOGNITION

23 --------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost | Pstart| Pstop --------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 22 | | 1374K| | | 1 | SORT AGGREGATE | | 1 | 22 | | | | |* 2 | HASH JOIN | | 1450M| 29G| 3092M| 1374K| | | 3 | INDEX FAST FULL SCAN | PK_MEMBER_CONTRACT_MEMCONSYSID | 170M| 1139M| | 10628 | | | 4 | PARTITION RANGE ITERATOR| | | | | | 53 | 65 |* 5 | TABLE ACCESS FULL | MEMBER_RECOGNITION | 1450M| 20G| | 178K| 53 | 65 --------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("MR"."MEMBER_CONTRACT_SYSID"="MC"."MEMBER_CONTRACT_SYSID") 5 - filter("MR"."RECOGNITION_DATE">TO_DATE('2006-01-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')) Another way to retrieve your Plan: SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY()) Or: @?/rdbms/admin/utlxpls Or: @/home/mruckdas/sql/plan_out Will Produce This:

24 To Run Explain Plan: Create One In Your Schema Using: @?/rdbms/admin/utlxplan Or: Gain Access to an existing one by someone granting you access and create a synonym CREATE SYNONYM PLAN_TABLE FOR COMMON_AREA.PLAN_TABLE; You need access to a PLAN_TABLE

25 How to read Explain Plan Output: First, Understand the following: Table Access Methods Table Join Methods Table Join Order

26 Table Access Methods ▬ Index Unique Scans ▬ Index Range Scans ▬ Index Range Scans Descending ▬ Index Skip Scans ▬ Full Scans ▬ Fast Full Index Scans ▬ Index Joins ▬ Bitmap Joins ▬ Bitmap AND / OR Full Table Scans Rowid Scans Index Scans Bitmap Access Cluster Scans Hash Scans

27 Lack of Index Large Amount of Data Small Table Parallelism FULL Hint Given When does this Occur? Full Table Scan This type of scan reads all rows from a table and filters out all rows which do not meet the WHERE clause criteria. This involves one logical read per row.

28 Rowid Scans A second step. Oracle first obtains the rowids of selected rows either from the table’ WHERE clause, or through an index scan of table indexes. Locating a row by specifying its rowid is the fastest way to retrieve a single row. Access by rowid might be required to retrieve any columns which are not present in an index. Not Always Necessary on index access. Definition: A Rowid specifies a datafile and the data block containing a particular Row and the location of the row in that block.

29 Index Scans A row is retrieved by traversing the index, using the indexed column values specified by the statement. An index scan retrieves data from an index based on the value of one or more columns in the index. Index access involves at least two logical reads per row. Consider the following Diagram of the B-Tree Index Structure: There are several types of Index Scans: B-Tree Index Organization

30 Index Unique Scan This scan returns, at most, a single rowid. This scan may be done when there is a UNIQUE or PRIMARY KEY constraint on the table containing the search key. This type of scan is used when all columns of a unique B-Tree index are specified using an equality condition. May be forced with the INDEX hint. When Used:

31 Index Range Scan This scan is used for accessing selective data. The range may be bounded or unbounded. When Used: This type of scan is used when one or more leading- columns of an index is specified in conditions such as: Col1 = :b1 Col1 < :b1 Col1 > :b1 Col1 LIKE ‘ABC%’ AND combination of the preceding conditions for leading columns in the index May be forced with the INDEX hint.

32 Index Range Scan Descending Identical to Index Range Scan, except that data is returned in descending order. When Used: This type of scan is used when ordering data in descending order and that order may be satisfied by an index or when seeking a value less than a specified value. By Default, indexed data is returned ascending order. May be forced with the INDEX_DESC hint.

33 Index Skip Scan This scan is used for composite indexes where the leading-column of the index is not specified in the predicate. The index is split into logically smaller sub-indexes. When Used: This type of scan is used when the leading-column of an index is not specified in the conditions. Skip Scanning is advantageous if there are few distinct values in the leading column of the composite index, and many distinct values in the non-leading key.

34 Index Full Scan This is like a Full table scan, but Oracle only reads Index blocks, one at a time. Typically scanning index blocks is faster than scanning table blocks. When Used: This scan is used if a predicate references one of the columns in the index, not necessarily the leading column. Or: If there is no predicate reference and: All columns in the table referenced are in the index At least one of the indexed columns is NOT NULL. A Full Scan may be used to eliminate a sort operation.

35 Index Fast Full Scan This is an alternative to a FULL table scan, when the index contains all columns that are needed by the query, and at least one index column is NOT NULL This scan accesses data in the index itself, without accessing the table. The entire index is read using multi-block reads and can be parallelized. Data is NOT returned in the index order. Restrictions: At least one indexed column must have the NOT NULL constraint There must be a parallel clause on the Index if FFS in parallel is desired. The index must have been analyzed. May be forced using the INDEX_FFS hint

36 Index Joins This is a hash join of several indexes that collectively contain all of the table columns that are referenced in the query. No table access is thereby needed. May be forced with the INDEX_JOIN hint A bitmap join uses a bitmap for key values and a mapping function that converts each bit position to a rowid. Bitmaps can efficiently merge indexes that correspond to several conditions in a WHERE clause, using Boolean operations to resolve AND and OR conditions. Bitmap Joins

37 Bitmap Table Access This is a special Method of accessing a table whereby Oracle will combine the use of multiple B-Tree indexes according to criteria in the WHERE Clause. Oracle will only consider if there is at least 1 bitmap index on the table. Can involve combinations of B-Tree and Bitmap, but needn’t involve any Bitmap Idx Rowids contained in B-Tree Indexes must be converted to bitmaps. After conversion, Boolean AND/OR operations for Bitmaps may be used. May be forced using the INDEX_COMBINE hint

38 This could produce a plan like: SELECT STATEMENT SORT AGGREGATE BITMAP CONVERSION COUNT BITMAP OR BITMAP AND BITMAP INDEX c1_ind SINGLE VALUE BITMAP CONVERSION FROM ROWIDS INDEX c2_ind RANGE SCAN BITMAP CONVERSION FROM ROWIDS SORT ORDER BY INDEX c3_ind RANGE SCAN

39 How does the CBO choose the Access Path? The optimizer first determines which access paths are available by examining the conditions in the statement's WHERE clause and its FROM clause 2. The optimizer then generates a set of possible execution plans using available access paths and estimates the cost of each plan, using the statistics for the index, columns, and tables accessible to the statement 2. The lowest estimated cost wins.

40 Join Methods Nested Loop Joins Hash Joins Sort Merge Joins Cartesian Joins Outer Joins

41 Nested Loop Join One table is designated as an outer (Driving) table, and the other the inner table. All matching rows in the inner table are accessed for each row in the outer table. Consider the following Nested Loop: For X in 1 to 10 Loop (Outer) For Y in 1 to 10 Loop (Inner) i = i + 1 print i: X, Y End Loop

42 Nested Loop Join (cont’d) Ideally, the inner table access is some type of index access, or the table s/b very small. Otherwise, a Hash Join may be more efficient. The order of tables in this type of Join is Very Important. Nested Loops may be Nested inside other Nested Loops.

43 Hash Join This Join Method is used to join large data sets. The optimizer uses the smaller of two data sources to build a hash table, in Memory, if possible, on the Join Key. The optimizer scans the larger table, probing the hash table to find the joined rows. Hash Table: Definition The join key is hashed to an index entry in a hash table. The hash table is an array and the row that goes with the hash key is stored in that entry.

44 Hash Join (cont’d) When Used: The Optimizer will use a hash join to join two tables if they are joined using an equi-join (i.e. a.col1 = b.col1) And if either of the following conditions are met: A large amount of data needs to be joined. A large fraction of the table needs to be joined. Hash Joins can be effective when the lack of a useful index renders Nested Loop joins ineffective. 3

45 Sort Merge Join Can be used to join rows from two independent sources. Generally, perform worse than Hash Joins, except if the following are true: –The Row Sources are sorted already –A Sort Operation does not have to be done. Benefit may be lost if slower table access method is chosen (e.g. Full Table Scan) Useful when an inequality condition (i.e., or >=) is selected

46 Sort Merge Join (cont’d) Sort / Merge Joins perform better than Nested Loops Joins for large data sets. Two Steps: (No Driving Table as in NL Join) 1.Sort: Both inputs are Sorted on Join Key 2.Merge: Two sorted lists are merged

47 Sort Merge Join (cont’d) When Used: –When Join Condition is not an equi-join –Parameter OPTIMIZER_MODE is set to RULE –Parameter HASH_JOIN_ENABLE is set to FALSE –Because data must already be sorted by other operations, optimizer thinks SJ is cheaper than HJ –Estimated Cost: Optimizer thinks SJ cost less than HJ cost based on HASH_AREA_SIZE and SORT_AREA_SIZE –The USE_MERGE hint has been specified.

48 Cartesian Join A join where every row from one data source is joined with every row from another data source. This creates a Cartesian Product. Happens when a table is listed in the FROM clause, but not joined to any other table in the WHERE clause. Generally, “BAD NEWS!”, resulting from poorly written SQL Sometimes, rarely, the optimizer thinks this type of join is less costly and induces.

49 Outer Join Extends the results of a simple join. Returns all rows that satisfy the Join Condition plus some or all rows from one table where the join condition is not satisfied. Similar But Different Types: Nested Loop Outer Join Hash Join Outer Join Sort Merge Outer Join Full Outer Join

50 Join Order Now we need to know how to analyze an Explained Plan and understand what is done when. Remember our Nested Loop example: For X in 1 to 10 Loop (Outer) For Y in 1 to 10 Loop (Inner) i = i + 1 print i: X, Y End Loop

51 Join Order (Cont’d) Q:What is the first real instruction executed? A: i = i + 1 The first innermost item from the top is the first instruction executed. For X in 1 to 10 Loop (Outer) For Y in 1 to 10 Loop (Inner) i = i + 1 print i: X, Y End Loop

52 Join Order (Cont’d) Similarly: 1.1 SELECT STATEMENT Optimizer=CHOOSE(Cost=23585 Card=1 Bytes=12) 2.1 SORT (AGGREGATE) 3.1 NESTED LOOPS (Cost=23585 Card=153843400 Bytes=1846120800) 4.1 INDEX (FAST FULL SCAN) OF 'MC_MCAS_IDX' (NON-UNIQUE) (Cost=13745 Card=153843400 Bytes=923060400) 4.2 INDEX (UNIQUE SCAN) OF 'PK_MEMBER_CONTRACT_ATTRIB' (UNIQUE) (Cost=1 Card=1 Bytes=6) First Step? 4.1 Index Access (Fast Full Scan) Then what? For the first value returned by 4.1, and every other, 4.2 is performed. 3.1 Indicates that for every row 4.1 returns 4.2 is repeated to EOT (Index) 4.1 2.1 Sort (Aggregate) = COUNT(1)

53 ID Query Plan ----------------------------------------------------------------------------------------------------------------- 1 | Select Statement Choose Cost=743,837 Rows=27,748 COST/ROW = 27 2 | Sort Group By Cost=743,837 Rows=27,748 3* | Hash Join Cost=743,528 Rows=27,748 4 | View Cost=287,095 Rows=5,772 5 | Sort Group By Cost=287,095 Rows=5,772 6* | Hash Join Cost=286,920 Rows=106,262 7* | Hash Join Cost=286,616 Rows=106,262 8 | Table Access (Full) of REJ_CRED_MBRS_BAL Analyzed Cost=2 Rows=5,772 9 | Table Access (Full) of FIN_TRANS Analyzed Cost=272,992 Rows=655,903,400 10 | Table Access (Full) of FTC_INV Analyzed Cost=91 Rows=697,780 11 | View Cost=456,424 Rows=27,748 12 | Sort Group By Cost=456,424 Rows=27,748 13* | Hash Join Cost=456,189 Rows=27,748 14* | Hash Join Cost=445,299 Rows=776,936 15 | Merge Join Cartesian Cost=23,090 Rows=161,616 16 | Nested Loops Cost=11,546 Rows=5,772 17 | Table Access (Full) of REJ_CRED_MBRS_BAL Analyzed Cost=2 Rows=5,772 18 | Table Access (By Index Rowid) of MEMBER_CONTRACT Analyzed Cost=2 Rows=1 19* | Unique Index (Unique Scan) of PK_MEMBER_CONTRACT_MEMCONSYSID Analyzed Cost=1 Rows=1 20 | Buffer Sort Cost=23,088 Rows=28 21 | Table Access (Full) of REFUND_METHOD Analyzed Cost=2 Rows=28 22 | Table Access (Full) of AR_DISTRIB_DETAILS Analyzed Cost=124,239 Rows=784,295,220 23 | Table Access (Full) of MEMBER_CONTRACT_ATTRIB Analyzed Cost=4,744 Rows=10,223,240 Predicate Information (identified by operation id): -------------------------------------------------- 3 - Access - ("A"."MEMBER_CONTRACT_SYSID"="B"."MEMBER_CONTRACT_SYSID") 6 - Access - ("FI"."FTC_INV_SYSID"="FT"."FTC_INV_SYSID") 7 - Access - ("A"."MEMBER_CONTRACT_SYSID"="FT"."MEMBER_CONTRACT_SYSID") 13 - Access - ("MC"."CUR_MEM_CONTRACT_ATTRIB_SYSID"="MCA"."MEMBER_CONTRACT_ATTRIB_SYSID“ AND "MCA"."REFUND_METHOD_ID"="RM"."REFUND_METHOD_ID") 14 - Access - ("MC"."MEMBER_CONTRACT_SYSID"="ARDD"."MEMBER_CONTRACT_SYSID") 19 - Access - ("A"."MEMBER_CONTRACT_SYSID"="MC"."MEMBER_CONTRACT_SYSID") ~/src/tuning_requests/2006_requests/20060823_bsperling/plan_orig_query.10835.log Let’s examine a few:

54 ID Query Plan ----------------------------------------------------------------------------------------------- 1 | Insert Statement Choose Cost=1,779,896 Rows=1,128,413 COST/ROW = 2 2 | Sort Group By Cost=1,779,896 Rows=1,128,413 3* | Hash Join Cost=1,769,786 Rows=1,128,413 4 | Table Access (Full) of PARTNER_CORPORATION Analyzed Cost=2 Rows=1,379 5* | Hash Join Cost=1,769,747 Rows=1,128,413 6 | Table Access (Full) of PARTNER Analyzed Cost=3 Rows=4,056 7* | Hash Join Cost=1,769,677 Rows=1,128,413 8* | Table Access (Full) of MEMBER_CONTRACT_ATTRIB Analyzed Cost=4,744 Rows=851,937 9* | Hash Join Cost=1,763,458 Rows=1,128,413 10 | Table Access (Full) of CA_MONTHLY_MEMBERS Analyzed Cost=3 Rows=21,400 11* | Hash Join Cost=1,757,904 Rows=27,603,992 12* | Table Access (Full) of MEMBER_CONTRACT Analyzed Cost=51,344 Rows=529,252 13 | Partition Range Iterator Partitions: KEY - 65 14* | Table Access (Full) of MEMBER_RECOGNITION Analyzed Cost=764,775 Rows=2,022,571,722 Partitions: KEY - 65 Predicate Information (identified by operation id): -------------------------------------------------- 3 - Access - ("P"."PARTNER_CORPORATION_SYSID"="PC"."PARTNER_CORPORATION_SYSID") 5 - Access - ("MCA"."PARTNER_ID"="P"."PARTNER_SYSID") 7 - Access - ("MC"."CUR_MEM_CONTRACT_ATTRIB_SYSID"="MCA"."MEMBER_CONTRACT_ATTRIB_SYSID") 8 - Filter - ("MCA"."BILLING_METHOD_ID"=3) 9 - Access - ("A"."MEMBERSHIP_NUMBER"="MC"."MEM_CONTRACT_ID") 11 - Access - ("MR"."MEMBER_CONTRACT_SYSID"="MC"."MEMBER_CONTRACT_SYSID") 12 - Filter - ("MC"."SERVICE"='SHP' AND "MC"."TERM_COUNTER"<=11) 14 - Filter - ("MR"."CLOSED_DATE" IS NULL AND "MR"."RECOGNITION_DATE">'31-MAR-05') ~/src/tuning_requests/2006_requests/20060706_bsperling/plan_orig_query.7409.log

55 ID Query Plan ----------------------------------------------------------------------------------------------- 1 | Select Statement Choose Cost=10,429,451 Rows=401,107,070 COST/ROW = 0 2 | Sort Group By Cost=10,429,451 Rows=401,107,070 3 | Merge Join Cartesian Cost=93,689 Rows=401,107,070 4* | Table Access (By Index Rowid) of FIN_TRANS Analyzed Cost=75 Rows=1 5 | Nested Loops Cost=43,388 Rows=621 6 | Nested Loops Cost=9,188 Rows=456 7* | Hash Join Cost=4,232 Rows=118 8 | Nested Loops Cost=4,228 Rows=118 9* | Hash Join Cost=4,228 Rows=118 10 | Nested Loops Cost=3 Rows=28 11 | Table Access (By Index Rowid) of BILLING_METHOD Analyzed Cost=1 Rows=1 12* | Unique Index (Unique Scan) of XPKBILLING_METHOD Analyzed Rows=1 13 | Table Access (Full) of REFUND_METHOD Analyzed Cost=2 Rows=28 14* | Table Access (By Index Rowid) of MEMBER_CONTRACT_ATTRIB Analyzed Cost=4,224 Rows=1,421 15* | Non-Unique Index (Range Scan) of MEMCA_MEMEXPDT_IDX Analyzed Cost=88 Rows=40,928 16* | Unique Index (Unique Scan) of XPKTERM_TYPE Analyzed Rows=1 17 | Table Access (Full) of BILLING_GROUP Analyzed Cost=3 Rows=3,042 18* | Table Access (By Index Rowid) of MEMBER_CONTRACT Analyzed Cost=42 Rows=4 19* | Non-Unique Index (Range Scan) of MC_MCAS_IDX Analyzed Cost=2 Rows=75 20* | Non-Unique Index (Range Scan) of FIN_TRANS_MEMCONSYSID Analyzed Cost=2 Rows=19 21 | Sort Aggregate Rows=1 22 | Partition Range Iterator Partitions: KEY - KEY 23* | Table Access (By Local Index Rowid) of AR_AP_FIN_LINK Analyzed Cost=16,658 Rows=260,943 Partitions: KEY - KEY 24* | Non-Unique Index (Range Scan) of AR_AP_FIN_LINK_PROCDATE Analyzed Cost=3,488 Rows=2,818,189 Partitions: KEY - KEY 25 | Buffer Sort Cost=10,429,376 Rows=646,320 26 | Unique Index (Fast Full Scan) of FTC_INV_TYPNUMSEQ Analyzed Cost=81 Rows=646,320 Predicate Information (identified by operation id): -------------------------------------------------- 4 - Filter - ("FT"."FIN_TRANS_TYPE"<>'J' AND "FT"."FIN_TRANS_TYPE"<>'T' AND "FT"."FIN_TRANS_SYSID">= (SELECT /*+ */ MIN("AR_AP_FIN_LINK"."FIN_TRANS_SYSID") FROM "FDS"."AR_AP_FIN_LINK" "AR_AP_FIN_LINK" WHERE "AR_AP_FIN_LINK"."PROCESSED_DATE"<"INFOCTL"."CYCLE_DATE"."CYCLE_DATE"()+1 AND "AR_AP_FIN_LINK"."PROCESSED_DATE">"INFOCTL"."CYCLE_DATE"."CYCLE_DATE_BOM"() AND "AR_AP_FIN_LINK"."PROCESS_CODE"='F')) 7 - Access - ("MCA"."BILLING_GROUP_SEQ_NUM"="BG"."BILLING_GROUP_SYSID") 9 - Access - ("MCA"."BILLING_METHOD_ID"="BM"."BILLING_METHOD_ID" AND "MCA"."REFUND_METHOD_ID"="RM"."REFUND_METHOD_ID") 12 - Access - ("BM"."BILLING_METHOD_ID"=3) 14 - Filter - ("MCA"."BILLING_METHOD_ID"=3 AND "MCA"."COMPANY_CODE"<>'NON') 15 - Access - ("MCA"."MEM_EXP_DATE">ADD_MONTHS("INFOCTL"."CYCLE_DATE"."CYCLE_DATE_EOM"(),-3) AND "MCA"."MEM_EXP_DATE"<"INFOCTL"."CYCLE_DATE"."CYCLE_DATE_EOM"()) 16 - Access - ("MCA"."TERM_TYPE_ID"="TT"."TERM_TYPE_ID") 18 - Filter - ("MC"."RECORD_ADD_DATE">"INFOCTL"."CYCLE_DATE"."CYCLE_DATE_BOM"()) 19 - Access - ("MC"."CUR_MEM_CONTRACT_ATTRIB_SYSID"="MCA"."MEMBER_CONTRACT_ATTRIB_SYSID") 20 - Access - ("MC"."MEMBER_CONTRACT_SYSID"="FT"."MEMBER_CONTRACT_SYSID") 23 - Filter - ("AR_AP_FIN_LINK"."PROCESS_CODE"='F') 24 - Access - ("AR_AP_FIN_LINK"."PROCESSED_DATE">"INFOCTL"."CYCLE_DATE"."CYCLE_DATE_BOM"() AND "AR_AP_FIN_LINK"."PROCESSED_DATE"<"INFOCTL"."CYCLE_DATE"."CYCLE_DATE"()+1) ~/src/tuning_requests/2006_requests/20060511_bsperling/plan_orig_query.21000.log

56 Ok. So we know how to read a Plan… Now What? Where to go from here? How do we Tune the SQL?

57 Where to go from here? Remember: In most Cases, Oracle will make the right decision. “If it ain’t broke, don’t fix it!” The single most-effective way to tune any SQL statement: … reduce the amount of work that Oracle needs to do, to return the desired result Your Tuning Goals: What is acceptable performance? Achieve it and move on!!!!

58 Where to go from here? (Cont’d) Use these to evaluate the plan. 1. Look at Oracle’ chosen join order. With your understanding of the underlying data, does the initial join produce the smallest result set first? Does the table Oracle decides to start with make sense (i.e. Will produce the smallest data set, or could be easily joined to an indexed column?) Is there any Join Criteria that you could add to give Oracle an opportunity make more selective joins (e.g. Smaller Result Set initially)

59 Where to go from here? (Cont’d) 2. Look at Oracle’ chosen join type for each set. With your understanding of the underlying data, and the query, does Oracle select the right join type for each set of data? Are large data sets being joined (i.e. Hash Join is better)? Is the “driving data set” sufficiently small, and an index available, to support Nested Loops? Did you expect Index access, and got Full Table scan, or vice-versa? Is Oracle smarter than you? (e.g. Did it make the right decision?)

60 Where to go from here? (Cont’d) 3. Look at Oracle’ chosen method of table access. With your understanding of the underlying data, and the query, does Oracle select the right method to access each table? Did you expect Index access, and got Full Table scan, or vice-versa? Is Oracle smarter than you? (e.g. Did it make the right decision?)

61 Where to go from here? (Cont’d) 3. Oracle’ chosen method of table access. (cont’d) What Filters are in the Where clause to limit data? Is Oracle starting with the Filtered Data? Could you add selection criteria to the WHERE clause to cause the amount of data selected (and joined) to be reduced? Could you avoid large amounts of data being needlessly scanned by adding a partition key to your selection criteria?

62 Where to go from here? (Cont’d) 4. Check Oracle’ Assumptions With your understanding of the underlying data, and the query, does Oracle make the correct assumptions (estimates) regarding the amount of data that will be returned at each step? Consider the following example:

63 SELECT sub.row_id AS subscription_id,pkg.row_id AS pkg_row_id,inv.row_id inv_row_id,pay.con_id,pay.actl_pay_dt,pay.x_mlov_pay_type_cd,pay.x_mlov_status_cd,pay.pay_amt,pkg.x_billing_cycle,pl.x_pay_freq,pli.x_membership_fee_trial,sub.x_promo_cd,sub.created FROM siebel.s_src_payment pay,siebel.s_invoice inv,siebel.s_pri_lst_item pli,siebel.s_pri_lst pl,siebel.s_agree_item sub,siebel.s_prod_int pkg WHERE pkg.prod_cd = 'Package' AND pkg.x_pkg_type IS NOT NULL AND pkg.row_id = sub.prod_int_id AND pl.row_id = sub.pri_lst_id AND pkg.row_id = pli.prod_id AND pli.pri_lst_id = sub.pri_lst_id AND sub.row_id = inv.agree_item_id AND inv.row_id = pay.pr_invoice_id AND pay.pay_amt > 0 AND pay.pay_amt <> pli.x_membership_fee_trial AND pay.x_pay_flow_type_cd = 'Debit' AND pay.x_transact_reason = 'Fees' AND pay.x_mlov_status_cd <> 'Cancelled' ORDER BY sub.row_id,pay.actl_pay_dt,inv.row_id Consider this SQL…

64 ID Query Plan --------------------------------------------------------------------------------------------------------------------- 1 | Select Statement Choose Cost=16,091 Rows=741 COST/ROW = 22 2 | Sort Order By Cost=16,091 Rows=741 3 | Nested Loops Cost=16,056 Rows=741 4 | Nested Loops Cost=16,049 Rows=741 5* | Hash Join Cost=15,376 Rows=22,429 6 | Nested Loops Cost=6,514 Rows=155,073 7 | Nested Loops Cost=5 Rows=353 8* | Table Access (By Index Rowid) of S_PROD_INT Analyzed Cost=1 Rows=78 9* | Non-Unique Index (Skip Scan) of S_PROD_INT_F11 Analyzed Cost=13 Rows=1,019 10 | Table Access (By Index Rowid) of S_PRI_LST_ITEM Analyzed Cost=1 Rows=5 11* | Unique Index (Range Scan) of S_PRI_LST_ITEM_U1 Analyzed Cost=2 Rows=5 12* | Table Access (By Index Rowid) of S_AGREE_ITEM Analyzed Cost=6,514 Rows=440 13 | And-Equal 14* | Non-Unique Index (Range Scan) of S_AGREE_ITEM_F5 Analyzed 15* | Non-Unique Index (Range Scan) of S_AGREE_ITEM_F1 Analyzed Cost=714 Rows=134,525 16* | View () of index$_join$_002 Cost=6,187 Rows=5,019,953 17* | Hash Join Cost=15,376 Rows=22,429 18* | Non-Unique Index (Fast Full Scan) of S_INVOICE_F20 Analyzed Cost=263 Rows=5,019,953 19 | Unique Index (Fast Full Scan) of S_INVOICE_P1 Analyzed Cost=263 Rows=5,019,953 20* | Table Access (By Index Rowid) of S_SRC_PAYMENT Analyzed Cost=1 Rows=1 21* | Non-Unique Index (Range Scan) of S_SRC_PAYMENT_V1 Analyzed Cost=2 Rows=1 22 | Table Access (By Index Rowid) of S_PRI_LST Analyzed Cost=1 Rows=1 23* | Unique Index (Unique Scan) of S_PRI_LST_P1 Analyzed Rows=1 Predicate Information (identified by operation id): ----------------------------------------------------------------------- 5 - Access - ("SUB"."ROW_ID"="INV"."AGREE_ITEM_ID") 8 - Filter - ("PKG"."X_PKG_TYPE" IS NOT NULL) 9 - Access - ("PKG"."PROD_CD"='Package') Filter - ("PKG"."PROD_CD"='Package') 11 - Access - ("PKG"."ROW_ID"="PLI"."PROD_ID") 12 - Filter - ("PKG"."ROW_ID"="SUB"."PROD_INT_ID" AND "PLI"."PRI_LST_ID"="SUB"."PRI_LST_ID") 14 - Access - ("PLI"."PRI_LST_ID"="SUB"."PRI_LST_ID") 15 - Access - ("PKG"."ROW_ID"="SUB"."PROD_INT_ID") 16 - Filter - ("INV"."AGREE_ITEM_ID" IS NOT NULL) 17 - Access - ("indexjoin$_alias$_008".ROWID="indexjoin$_alias$_007".ROWID) 18 - Filter - ("indexjoin$_alias$_007"."AGREE_ITEM_ID" IS NOT NULL) 20 - Filter - ("PAY"."PAY_AMT">0 AND "PAY"."PAY_AMT"<>"PLI"."X_MEMBERSHIP_FEE_TRIAL" AND "PAY"."X_PAY_FLOW_TYPE_CD"='Debit‘ AND "PAY"."X_TRANSACT_REASON "='Fees' AND "PAY"."X_MLOV_STATUS_CD"<>'Cancelled') 21 - Access - ("INV"."ROW_ID"="PAY"."PR_INVOICE_ID") 23 - Access - ("PL"."ROW_ID"="SUB"."PRI_LST_ID") ~/src/tuning_requests/2008_requests/2008_04_04_siebel_lrq With this Plan…

65 SELECT COUNT(1) FROM siebel.s_prod_int pkg,siebel.s_pri_lst_item pli WHERE pkg.prod_cd = 'Package' AND pkg.x_pkg_type IS NOT NULL AND pkg.row_id = pli.prod_id /... COUNT(1) ---------- 1361 1 row selected. Elapsed: 00:00:01.22 No errors. run_sql.sh : End : Mon Aug 11 17:34:42 EDT 2008

66 ID Query Plan --------------------------------------------------------------------------------------------------------------------- 1 | Select Statement Choose Cost=16,091 Rows=741 COST/ROW = 22 2 | Sort Order By Cost=16,091 Rows=741 3 | Nested Loops Cost=16,056 Rows=741 4 | Nested Loops Cost=16,049 Rows=741 5* | Hash Join Cost=15,376 Rows=22,429 6 | Nested Loops Cost=6,514 Rows=155,073 7 | Nested Loops Cost=5 Rows=353 8* | Table Access (By Index Rowid) of S_PROD_INT Analyzed Cost=1 Rows=78 9* | Non-Unique Index (Skip Scan) of S_PROD_INT_F11 Analyzed Cost=13 Rows=1,019 10 | Table Access (By Index Rowid) of S_PRI_LST_ITEM Analyzed Cost=1 Rows=5 11* | Unique Index (Range Scan) of S_PRI_LST_ITEM_U1 Analyzed Cost=2 Rows=5 12* | Table Access (By Index Rowid) of S_AGREE_ITEM Analyzed Cost=6,514 Rows=440 13 | And-Equal 14* | Non-Unique Index (Range Scan) of S_AGREE_ITEM_F5 Analyzed 15* | Non-Unique Index (Range Scan) of S_AGREE_ITEM_F1 Analyzed Cost=714 Rows=134,525 16* | View () of index$_join$_002 Cost=6,187 Rows=5,019,953 17* | Hash Join Cost=15,376 Rows=22,429 18* | Non-Unique Index (Fast Full Scan) of S_INVOICE_F20 Analyzed Cost=263 Rows=5,019,953 19 | Unique Index (Fast Full Scan) of S_INVOICE_P1 Analyzed Cost=263 Rows=5,019,953 20* | Table Access (By Index Rowid) of S_SRC_PAYMENT Analyzed Cost=1 Rows=1 21* | Non-Unique Index (Range Scan) of S_SRC_PAYMENT_V1 Analyzed Cost=2 Rows=1 22 | Table Access (By Index Rowid) of S_PRI_LST Analyzed Cost=1 Rows=1 23* | Unique Index (Unique Scan) of S_PRI_LST_P1 Analyzed Rows=1 Predicate Information (identified by operation id): ----------------------------------------------------------------------- 5 - Access - ("SUB"."ROW_ID"="INV"."AGREE_ITEM_ID") 8 - Filter - ("PKG"."X_PKG_TYPE" IS NOT NULL) 9 - Access - ("PKG"."PROD_CD"='Package') Filter - ("PKG"."PROD_CD"='Package') 11 - Access - ("PKG"."ROW_ID"="PLI"."PROD_ID") 12 - Filter - ("PKG"."ROW_ID"="SUB"."PROD_INT_ID" AND "PLI"."PRI_LST_ID"="SUB"."PRI_LST_ID") 14 - Access - ("PLI"."PRI_LST_ID"="SUB"."PRI_LST_ID") 15 - Access - ("PKG"."ROW_ID"="SUB"."PROD_INT_ID") 16 - Filter - ("INV"."AGREE_ITEM_ID" IS NOT NULL) 17 - Access - ("indexjoin$_alias$_008".ROWID="indexjoin$_alias$_007".ROWID) 18 - Filter - ("indexjoin$_alias$_007"."AGREE_ITEM_ID" IS NOT NULL) 20 - Filter - ("PAY"."PAY_AMT">0 AND "PAY"."PAY_AMT"<>"PLI"."X_MEMBERSHIP_FEE_TRIAL" AND "PAY"."X_PAY_FLOW_TYPE_CD"='Debit‘ AND "PAY"."X_TRANSACT_REASON "='Fees' AND "PAY"."X_MLOV_STATUS_CD"<>'Cancelled') 21 - Access - ("INV"."ROW_ID"="PAY"."PR_INVOICE_ID") 23 - Access - ("PL"."ROW_ID"="SUB"."PRI_LST_ID") ~/src/tuning_requests/2008_requests/2008_04_04_siebel_lrq

67 SELECT COUNT(1) FROM siebel.s_prod_int pkg,siebel.s_pri_lst_item pli,siebel.s_agree_item sub WHERE pkg.prod_cd = 'Package' AND pkg.x_pkg_type IS NOT NULL AND pkg.row_id = pli.prod_id AND sub.pri_lst_id = pli.pri_lst_id /... COUNT(1) ---------- 18958864 1 row selected. Elapsed: 00:03:39.73 No errors. run_sql.sh : Begin : Mon Aug 11 18:06:24 EDT 2008 6 | Nested Loops Cost=6,514 Rows=155,073 7 | Nested Loops Cost=5 Rows=353 8* | Table Access (By Index Rowid) of S_PROD_INT Analyzed Cost=1 Rows=78 9* | Non-Unique Index (Skip Scan) of S_PROD_INT_F11 Analyzed Cost=13 Rows=1,019 10 | Table Access (By Index Rowid) of S_PRI_LST_ITEM Analyzed Cost=1 Rows=5 11* | Unique Index (Range Scan) of S_PRI_LST_ITEM_U1 Analyzed Cost=2 Rows=5 12* | Table Access (By Index Rowid) of S_AGREE_ITEM Analyzed Cost=6,514 Rows=440 13 | And-Equal 14* | Non-Unique Index (Range Scan) of S_AGREE_ITEM_F5 Analyzed 15* | Non-Unique Index (Range Scan) of S_AGREE_ITEM_F1 Analyzed Cost=714 Rows=134,525 1 SELECT table_name 2,owner 3,num_rows 4,partitioned 5 FROM dba_tables 6* WHERE table_name = UPPER('S_AGREE_ITEM') MRUCKDASCHEL@elizabeth_crmuk2 SQL> / TABLE_NAME OWNER NUM_ROWS PAR ------------------------------ -------------------- ---------- --- S_AGREE_ITEM SIEBEL 36494164 NO ~19M / 155K = 122 Times More rows than expected. ~19M / 36M = 52% of Rows in Table That is a significant difference!!!!

68 What are some ways we can alter this plan? Rewrite and/or modify the SQL: –Force Index Usage Elimination (WHERE) –Modify selection criteria to WHERE (i.e. Partition Key) –Use Correlated Sub-Query (EXISTS) rather than Join Alter the Database and/or Session (Not too common, other than Indexes) Add Hints (s/b last resort)

69 Plan Modification: 1.Enable Index Usage / Partition Pruning –If a Function is applied to an indexed column in the WHERE clause, the Index cannot be used. –Same goes for Partition Pruning. If a function is applied to the partition key column, partition pruning CANNOT occur. –Conversly, the application of a Function may be used eliminate the usage of an index. What is Partition Pruning??? On a partitioned table or index, if the partition’ key is referenced in the WHERE clause, Oracle will only access those Partitions that have that/those partition key value(s)

70 Plan Modification: (Cont’d) 2. Modify Selection Criteria to WHERE clause: –If Oracle is not using a Join Method, Join Order, or Table Access that you think should be used, you can try adding/deleting Join conditions which you know to be true (or are also true). –For example, if a Join is being done to a table that is redundant (e.g. A join on the Primary Key AND another set of columns from the same two tables, unnecessary Table Access may occur.

71 Plan Modification: (Cont’d) 3. Use correlated sub-query rather than join. Instead of coding this: SELECT MC.MEM_CONTRACT_ID FROM MEMBER_CONTRACTMC,MC_TEMP_TABLE MCT WHERE MCT.MC_SYSID = MC.MEMBER_CONTRACT_SYSID /

72 Plan Modification: (Cont’d) 3. Use correlated sub-query rather than join. Consider Doing This: SELECT MC.MEM_CONTRACT_ID FROM MEMBER_CONTRACTMC WHERE EXISTS ( SELECT 1 FROM MC_TEMP_TABLE MCT WHERE MCT.MC_SYSID = MC.MEMBER_CONTRACT_SYSID) / (Assuming idx on MCT.MC_SYSID)

73 Plan Modification: (Cont’d) 4. Hinting –Hints are not really “Hints” They are Directives!!!!! –Should be used as a “last Resort”. –Use CAUTION when storing hard-coded in application code. –Consider employing Parallelism

74 I’m probably over my time limit! We talked about how to understand what an Explain Plan is telling us Table Access Methods, Join Methods, and Join Orders We discussed how we can evaluate this plan, and alter it intelligently to achieve our objectives.


Download ppt "SQL Tuning 101 excerpt: Explain Plan A Logical Approach Michael Ruckdaschel Affinion Group International."

Similar presentations


Ads by Google