124
<Insert Picture Here>  Exadata SQL Pe rfor mance Tuning Detail Methodology

6 Huawei Exadata Practice 519819 Zhs

Embed Size (px)

DESCRIPTION

6 Huawei Exadata Practice 519819 Zhs

Citation preview

  • Exadata SQL Performance Tuning Detail Methodology

  • Agenda

    1. Introduction

    2. Oracle SQL Tuning Skill Set

    3. Basic Skill

    4. Recommended Methodology

    Case Study 1:

    Case Study 2:

    5. Additional SQL Tuning Idea

    6. Q&A

  • Introduction

    The purpose of this sharing session is to provide Huawei a

    generic & systematic method that will help Huaweis developersto find and fix a SQL performance problem in Exadata

    environment.

    The recommended methodology will attempt to guide developer

    along a process that will diagnose most common Oracle SQL

    performance problems.

  • Oracle SQL Tuning Skillset

    1. Basic Skill

    How to pick problematic SQL

    How to get the execution plan

    2. Advanced skill

  • - How to pick problematic SQL

    Identify the problematic SQL by:

    AWR report top elapsed time, top CPU time, top buffer get etc

    GV$SESSION &G V$SQL

    Enable 10046 trace at session level

    Report by user

  • - Automatic Workload Repository (AWR) in Oracle Database 11g

    Under SQL Statistics section, they are many useful

    information SQL ordered by Elapsed Time

    SQL ordered by CPU Time

    SQL ordered by User I/O Wait Time

    SQL ordered by Gets

    SQL ordered by Reads

    SQL ordered by Physical Reads (UnOptimized)

    SQL ordered by Executions

    SQL ordered by Parse Calls

    SQL ordered by Sharable Memory

    SQL ordered by Version Count

    SQL ordered by Cluster Wait Time

    Complete List of SQL Text

  • - Automatic Workload Repository (AWR) in Oracle Database 11g

    By example, under SQL ordered by Elapsed Time

    Easy to identify SQL that run very long time

  • - Automatic Workload Repository (AWR) in Oracle Database 11g

    In general,

    Tune SQL that Elapsed Time per execution is high

    Tune SQL that Buffer Get per execution is high

    Tune SQL that CPU Time per execution is high

    Ask DBA for the AWR report

  • - How to pick problematic SQL

    If the SQL is still running, query GV$SESSION and GV$SQL

    Example :select g.inst_id, g.sid, g.serial#, g.sql_id, g.event,

    g.machine, g.sql_exec_start, l.sql_text,

    l.PLAN_HASH_VALUE, g.blocking_session, g.SERVICE_NAME,

    g.status, g.LOGON_TIME

    from gv$session g , (select distinct sql_id, CHILD_NUMBER,

    PLAN_HASH_VALUE, SQL_text from gv$sql) l

    where g.username is not null and status = 'ACTIVE'

    and g.sql_id = l.SQL_ID and g.SQL_CHILD_NUMBER =

    l.CHILD_NUMBER

    order by g.sql_exec_start ;

  • - How to pick problematic SQL

    Identify problematic SQL that is running for a long time refer to SQL_EXEC_START

  • Oracle SQL Tuning Skillset

    1. Basic Skill

    How to pick problematic SQL

    How to get the execution plan

    2. Advanced skill

  • Why we need execution plan?

    For SQL tuning, we need to review and understand

    the execution plan.

    Understand the execution plan is more important than

    understand the program logic in SQL tuning

  • - How to get SQL execution plan

    If you get the problematic SQL from AWR, you can get the

    execution plan by running

    select * from

    table(Dbms_Xplan.display_awr(', null,

    null, 'ALL'))

  • - How to get SQL execution plan

    If you get the problematic SQL from AWR, you can get the

    execution plan by running

    SQL> select * from table(Dbms_Xplan.display_awr('0rbu5r3j5f7fg', null, null, 'ALL'))

    2 /

    PLAN_TABLE_OUTPUT

    --------------------------------------------------------------------------------

    SQL_ID 0rbu5r3j5f7fg

    --------------------

    SELECT DISTINCT T.COMPANY_ID, T.CONTRACT_NUMBER, T.PRODUCT_ID FROM

    BL_RCR_RATE_SEPCIAL_TMP1 T WHERE T.PERIOD_ID = :B2 AND T.SOURCE_TYPE_ID

    = :B1 AND T.COMPANY_ID IN (SELECT G.COMPANY_ID FROM

    APD_RCR_SCH_BATCH_COMPANY G WHERE G.BATCH_ID = :B3 ) AND

    T.PROGRAM_SOURCE IN ('REV_SPECIAL_PRE1', 'REV_SPECIAL_PRE2')

    Plan hash value: 2906480639

    --------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes |TempS

    --------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | | |

    | 1 | HASH UNIQUE | | 112K| 6819K| 886

    | 2 | NESTED LOOPS | | 112K| 6819K|

    | 3 | INDEX RANGE SCAN| APD_RCR_SCH_BATCH_COMPANY_U1 | 31 | 310 |

    | 4 | INDEX RANGE SCAN| BL_RCR_RATE_SEPCIAL_TMP1_N1 | 3633 | 184K|

    --------------------------------------------------------------------------------

    Query Block Name / Object Alias (identified by operation id):

    -------------------------------------------------------------

    1 - SEL$5DA710D3

    3 - SEL$5DA710D3 / G@SEL$2

    4 - SEL$5DA710D3 / T@SEL$1

  • - How to get SQL execution plan

    You can get SQL_ID - unique for each SQL statement and Plan

    hash value unique for each execution plan for this SQL for further tuning use.

    SQL> select * from table(Dbms_Xplan.display_awr('0rbu5r3j5f7fg', null, null, 'ALL'))

    2 /

    PLAN_TABLE_OUTPUT

    --------------------------------------------------------------------------------

    SQL_ID 0rbu5r3j5f7fg

    --------------------

    SELECT DISTINCT T.COMPANY_ID, T.CONTRACT_NUMBER, T.PRODUCT_ID FROM

    BL_RCR_RATE_SEPCIAL_TMP1 T WHERE T.PERIOD_ID = :B2 AND T.SOURCE_TYPE_ID

    = :B1 AND T.COMPANY_ID IN (SELECT G.COMPANY_ID FROM

    APD_RCR_SCH_BATCH_COMPANY G WHERE G.BATCH_ID = :B3 ) AND

    T.PROGRAM_SOURCE IN ('REV_SPECIAL_PRE1', 'REV_SPECIAL_PRE2')

    Plan hash value: 2906480639

    --------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes |TempS

    --------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | | |

    | 1 | HASH UNIQUE | | 112K| 6819K| 886

    | 2 | NESTED LOOPS | | 112K| 6819K|

    | 3 | INDEX RANGE SCAN| APD_RCR_SCH_BATCH_COMPANY_U1 | 31 | 310 |

    | 4 | INDEX RANGE SCAN| BL_RCR_RATE_SEPCIAL_TMP1_N1 | 3633 | 184K|

    --------------------------------------------------------------------------------

    Query Block Name / Object Alias (identified by operation id):

    -------------------------------------------------------------

    1 - SEL$5DA710D3

    3 - SEL$5DA710D3 / G@SEL$2

    4 - SEL$5DA710D3 / T@SEL$1

  • - How to get SQL execution plan

    If you get the problematic SQL from running session, you can view

    the execution plan by running DBMS_XPLAN in particular

    instance

    Set long 20000000

    Set pagesize 0

    Set linesize 200

    Select * from

    table(dbms_xplan.display_cursor(,null

    , ALLSTATS LAST))

  • - How to get SQL execution plan

    If you get the problematic SQL from runnnig session, you can view

    the execution plan by using SQL Monitor Report

    Set long 20000000

    Set pagesize 0

    Set linesize 200

    Select dbms_sqltune.report_sql_monitor(sql_id =>

    SQL_ID) from dual

  • - How to get SQL execution plan

    If you only know the package, enable trace 10046

    before execute the package

    1. Example:

    2. SQLPLUS>alter session set events 10046 trace name context forever, level 12;

    3. SQLPLUS>exec

    PKG_BL_RCR_REV_FACT.SP_BL_RCR_REV

    4. After done, go the user_dump_dest and get the trace file.

    5. OS> cd /u01/app/oracle/diag/rdbms/dwdb/DWDB1/trace

    6. OS> tkprof tracefile_name outputfile_name

    explain=hwdw/password waits=y

  • Oracle SQL Tuning Skillset

    1. Basic Skill

    How to pick problematic SQL

    How to get the execution plan

    2. Advanced skill

    Recommended Methodology

  • - Recommended Methodology

    Most common SQL problems that are easy to identify and easy

    to fix by below steps.

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    2. Does the SQL already have optimized joining method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove / add it and re-test

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or remove INDEX() or similar

    hints

    4. Does SQL has appropriate join predicates?

    A. If yes, go to next step

    B. If no, adding appropriate HINT

    5. For low volume SQLs, are there any Full Table/Partition Scans?

    A. If yes, check if appropriate to add index

    B. If no, go to next step

  • - Recommended Methodology

    Most common SQL problems that are easy to identify and easy

    to fix by below steps.

    6. Do you compare 9i, 10g and 11g execution plan?

    A. Compare each plans and bind the best to SPM

  • - Recommended Methodology

    Most common SQL problems that are easy to identify and easy

    to fix by below steps.

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    2. Does the SQL already have optimized joining method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove / add it and re-test

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or remove INDEX() or similar

    hints

    4. Does SQL has appropriate join predicates?

    A. If yes, go to next step

    B. If no, adding appropriate HINT

    5. For low volume SQLs, are there any Full Table/Partition Scans?

    A. If yes, check if appropriate to add index

    B. If no, go to next step

  • - Recommended Methodology

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

  • Why accurate statistic is so important?

    The cost based optimizer has the challenging job of

    evaluating any SQL statement and generating the

    "best" execution plan for the statement.

    Object metadata The DBA controls the quality of themetadata via the dbms_stats package. This data includes the

    number of rows in a table, the distribution of values within a

    column and other critical information about the state of the

    tables and indexes.

    Disk I/O speed The cost of disk I/O is the single mostimportant factor in SQL optimization. Disk I/O is measured in

    thousandths of a second, an eternity for a database, and

    something that needs to be avoided whenever possible.

  • Why accurate statistic is so important?

    Case study S1:

    SQL> SELECT SUM(MTA.BASE_TRANSACTION_VALUE) ABSORPTION_AMOUNT FROM

    ODS_GL_CODE_COMBINATIONS GCC, ODS_MTL_TRANSACTION_ACCOUNTS MTA WHERE

    MTA.REFERENCE_ACCOUNT = GCC.CODE_COMBINATION_ID(+) AND GCC.SEGMENT3

    LIKE '128%' AND MTA.ACCOUNTING_LINE_TYPE 15 AND MTA.COST_ELEMENT_ID

    1 AND MTA.TRANSACTION_ID = 2455022551;

  • Why accurate statistic is so important?

    Case study S1:

    SQL> SELECT SUM(MTA.BASE_TRANSACTION_VALUE) ABSORPTION_AMOUNT FROM

    ODS_GL_CODE_COMBINATIONS GCC, ODS_MTL_TRANSACTION_ACCOUNTS MTA WHERE

    MTA.REFERENCE_ACCOUNT = GCC.CODE_COMBINATION_ID(+) AND GCC.SEGMENT3

    LIKE '128%' AND MTA.ACCOUNTING_LINE_TYPE 15 AND MTA.COST_ELEMENT_ID

    1 AND MTA.TRANSACTION_ID = 2455022551;

    If No / Inaccurate table statistic:

    Delete statistic in tablesexec dbms_stats.delete_table_stats('ODSPUB', 'ODS_GL_CODE_COMBINATIONS')

    exec dbms_stats.delete_table_stats('ODSCST', 'ODS_MTL_TRANSACTION_ACCOUNTS')

  • Why accurate statistic is so important?

    Case study S1:

    Execution plan with no table statistic

    ---------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |

    ---------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 117 | 484 (1)| 00:00:07 | | |

    | 1 | SORT AGGREGATE | | 1 | 117 | | | | |

    |* 2 | HASH JOIN | | 22690 | 2592K| 484 (1)| 00:00:07 | | |

    |* 3 | TABLE ACCESS BY GLOBAL INDEX ROWID| ODS_MTL_TRANSACTION_ACCOUNTS | 22690 | 1440K| 2 (0)| 00:00:01 | ROWID | ROWID |

    |* 4 | INDEX RANGE SCAN | ODS_MTL_TRANSACTION_ACCOUNT_N2 | 262K| | 1 (0)| 00:00:01 | | |

    | 5 | TABLE ACCESS BY INDEX ROWID | ODS_GL_CODE_COMBINATIONS | 210K| 10M| 481 (0)| 00:00:07 | | |

    |* 6 | INDEX RANGE SCAN | ODS_GL_CODE_COMBINATIONS_N3 | 210K| | 448 (0)| 00:00:07 | | |

    ---------------------------------------------------------------------------------------------------------------------------------------

    1 rows selected.

    Elapsed: 00:00:08.00

    Why slow ? Because incorrect index was chosen

  • Why accurate statistic is so important?

    Case study S1:

    SQL> SELECT SUM(MTA.BASE_TRANSACTION_VALUE) ABSORPTION_AMOUNT FROM

    ODS_GL_CODE_COMBINATIONS GCC, ODS_MTL_TRANSACTION_ACCOUNTS MTA WHERE

    MTA.REFERENCE_ACCOUNT = GCC.CODE_COMBINATION_ID(+) AND GCC.SEGMENT3

    LIKE '128%' AND MTA.ACCOUNTING_LINE_TYPE 15 AND MTA.COST_ELEMENT_ID

    1 AND MTA.TRANSACTION_ID = 2455022551;

    With accurate table statistic:

    Gather statistic for tables using auto sample size, for all columns size

    autoexec

    dbms_stats.gather_table_stats(ownname=>'ODSCST',tabname=>'ODS_MTL_TRANSACTION_ACCO

    UNTS',estimate_percent=>dbms_stats.auto_sample_size, degree=>128, cascade=>True,

    method_opt=>'FOR ALL COLUMNS SIZE AUTO');

    exec

    dbms_stats.gather_table_stats(ownname=>'ODSPUB',tabname=>'ODS_GL_CODE_COMBINATIONS'

    ,estimate_percent=>dbms_stats.auto_sample_size, degree=>32, cascade=>True, method_opt=>'FOR

    ALL COLUMNS SIZE AUTO');

  • Why accurate statistic is so important?

    Case study S1:

    Execution plan with accurate table statistic

    ----------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |

    ----------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 37 | 9 (0)| 00:00:01 | | |

    | 1 | SORT AGGREGATE | | 1 | 37 | | | | |

    | 2 | NESTED LOOPS | | | | | | | |

    | 3 | NESTED LOOPS | | 2 | 74 | 9 (0)| 00:00:01 | | |

    |* 4 | TABLE ACCESS BY GLOBAL INDEX ROWID| ODS_MTL_TRANSACTION_ACCOUNTS | 2 | 46 | 5 (0)| 00:00:01 | ROWID | ROWID |

    |* 5 | INDEX RANGE SCAN | ODS_MTL_TRANSACTION_ACCOUNT_N2 | 4 | | 4 (0)| 00:00:01 | | |

    |* 6 | INDEX UNIQUE SCAN | ODS_GL_CODE_COMBINATIONS_U1 | 1 | | 1 (0)| 00:00:01 | | |

    |* 7 | TABLE ACCESS BY INDEX ROWID | ODS_GL_CODE_COMBINATIONS | 1 | 14 | 2 (0)| 00:00:01 | | |

    ----------------------------------------------------------------------------------------------------------------------------------------

    1 rows selected.

    Elapsed: 00:00:01.00

    Correct index was chosen with accurate statistic

  • Why accurate statistic is so important?

    Case study S2:

    SQL> SELECT * FROM table (DBMS_XPLAN.DISPLAY_CURSOR('&SQL_ID', NULL, 'ALLSTATS LAST'));

    Enter value for sql_id: 0wbgug6xbr7gg

    old 1: SELECT * FROM table (DBMS_XPLAN.DISPLAY_CURSOR('&SQL_ID', NULL, 'ALLSTATS LAST'))

    new 1: SELECT * FROM table (DBMS_XPLAN.DISPLAY_CURSOR('0wbgug6xbr7gg', NULL, 'ALLSTATS LAST'))

    PLAN_TABLE_OUTPUT

    ----------------------------------------------------------------------------------------

    SQL_ID 0wbgug6xbr7gg, child number 0

    -------------------------------------

    SELECT 830 RECORD_TYPE_ID, TMP.PERIOD_ID, TMP.COMPANY_ID,

    TMP.COMPANY_CODE, TMP.FIN_REGION_ID, TMP.REGION_CODE, TMP.ACCOUNT_ID,

    TMP.ACCOUNT_CODE, TMP.PHYSICAL_PRODUCT_ID, TMP.PHYSICAL_PRODUCT_CODE,

    TMP.SERVICE_PRODUCT_ID, TMP.SERVICE_PRODUCT_CODE, PCD.CUSTOMER_ID

    CUSTOMER_ID, TMP.CUSTOMER_CODE, TMP.PROJECT_ID, TMP.PROJECT_CODE,

    TMP.COST_CENTER_ID, TMP.CURRENCY_ID, TMP.FIN_CONTRACT_ID,

    TMP.CONTRACT_NUMBER, TMP.SUB_PROJECT_ID, TMP.SUB_PROJECT_CODE,

    TMP.EXPENSE_TYPE, TMP.SOURCE_TYPE, TMP.ATTRIBUTE1, TMP.ATTRIBUTE2,

    TMP.ATTRIBUTE3, TMP.ATTRIBUTE4, TMP.ATTRIBUTE5, TMP.ATTRIBUTE6,

    TMP.ATTRIBUTE7, TMP.SER_ASSIGN_STEP, TMP.PHY_ASSIGN_STEP,

    TMP.REPORT_TYPE_FLAG, TMP.INDUSTRY_CLASS_ID, NVL(TMP.AMOUNT, 0) AMOUNT,

    NVL(TMP.FUNC_AMOUNT, 0) FUNC_AMOUNT, NVL(TMP.RMB_AMOUNT, 0) RMB_AMOUNT

    FROM BL_GTS_CST_CLASSIFY_TMP TMP, BL_PROJECT_CUST_DIM PCD WHERE

    EXPENSE_TYPE IN ('C610', 'C630') AND TMP.PERIOD_ID = :B1 AND

    NVL(TMP.RMB_AMOUNT,0) 0 AND TMP.SUB_PROJECT_ID = PCD.PROJECT_ID(+)

  • Why accurate statistic is so important?

    Case study S2:

    -----------------------------------------------------------------------

    | Id | Operation | Name | E-Rows |

    -----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | |

    | 1 | NESTED LOOPS OUTER | | 1 |

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 1 |

    |* 3 | INDEX RANGE SCAN | BL_PROJECT_CUST_DIM_N1 | 1 |

    -----------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - storage(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    filter(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    3 - access("TMP"."SUB_PROJECT_ID"="PCD"."PROJECT_ID")

    Optimizer uses nested loop to join those tables

    because estimated row is small for both tables

  • Why accurate statistic is so important?

    Case study S2:SELECT COUNT(1) from BL_GTS_CST_CLASSIFY_TMP WHERE PERIOD_ID = 201103

    -----------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads

    -----------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.07 | 4294 | 4286

    | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.07 | 4294 | 4286

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 1 | 1 | 293K|00:00:00.07 | 4294 | 4286

    -----------------------------------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - storage("TMP"."PERIOD_ID"=201103)

    filter("TMP"."PERIOD_ID"=201103)

    Based on table statistic, E-rows (estimate row: 1) VS A-

    rows (actual rows: 293K) is huge different

    Why?

  • Why accurate statistic is so important?

    Table Actual number of

    record during execution

    BL_GTS_CST_CLASSIFY_TMP 30,395

    BL_PROJECT_CUST_DIM 196,557

    Rows (1st) Rows (avg) Rows (max) Row Source Operation

    ---------- ---------- ---------- ---------------------------------------------------

    30395 30395 30395 HASH JOIN OUTER (cr=3690 pr=3399 pw=0 time=1306343 us cost=647 size=6076431 card=30231)

    30395 30395 30395 TABLE ACCESS STORAGE FULL BL_GTS_CST_CLASSIFY_TMP (cr=2965 pr=2959 pw=0 time=44754 us

    cost=451 size=5743890 card=30231)

    196557 196557 196557 INDEX STORAGE FAST FULL SCAN BL_PROJECT_CUST_DIM_N1 (cr=725 pr=440 pw=0 time=56939 us

    cost=65 size=2162127 card=196557)

    Based on 10046 trace file, number of row processed is as above

  • Why accurate statistic is so important?

    Case study S2:-----------------------------------------------------------------------

    | Id | Operation | Name | E-Rows |

    -----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | |

    | 1 | NESTED LOOPS OUTER | | 1 |

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 1 |

    |* 3 | INDEX RANGE SCAN | BL_PROJECT_CUST_DIM_N1 | 1 |

    -----------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - storage(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    filter(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    3 - access("TMP"."SUB_PROJECT_ID"="PCD"."PROJECT_ID")

    Optimizer uses nested loop to join those tables but

    actual rows count for both tables are huge

  • Why is wrong with using Nested Loop for joining?

    Indexed Nested Loops is used primarily in low volume

    joins; it is efficient over small volumes and versatile

    enough to be used in a variety of situations.

    Although it is fully scalable, Indexed Nested Loops is

    inefficient over large data volumes.

  • Why accurate statistic is so important?

    What is wrong???

    Incorrect table statistic & histogram

  • Why accurate statistic is so important?

    Case study S2:SQL> select TABLE_NAME, COLUMN_NAME, LAST_ANALYZED, NUM_DISTINCT, LOW_VALUE, HIGH_VALUE, HISTOGRAM from dba_tab_columns

    where TABLE_NAME = 'BL_GTS_CST_CLASSIFY_TMP'

    TABLE_NAME COLUMN_NAME LAST_ANALYZED NUM_DISTINCT LOW_VALUE HIGH_VALUE HISTOG

    ------------------------------ ------------------------------ --------------- ------------ ---------- ---------- ------

    BL_GTS_CST_CLASSIFY_TMP PERIOD_ID 11-JUN-11 1 C3150C05 C3150C05 FREQUE

    SQL> select utl_raw.cast_to_number(high_value) from dba_tab_columns where table_name = 'BL_GTS_CST_CLASSIFY_TMP

    UTL_RAW.CAST_TO_NUMBER(HIGH_VALUE)

    ---------------------------------

    201104

    SQL> select utl_raw.cast_to_number(low_value) from dba_tab_columns where table_name = 'BL_GTS_CST_CLASSIFY_TMP

    UTL_RAW.CAST_TO_NUMBER(LOW_VALUE)

    ---------------------------------

    201104

    Checked the table histogram, the highest / lowest value in period_id column

    is 201104. Should have no 201103 data exist in the table.

  • Why accurate statistic is so important?

    Case study S2:-----------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads

    -----------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.07 | 4294 | 4286

    | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.07 | 4294 | 4286

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 1 | 1 | 293K|00:00:00.07 | 4294 | 4286

    -----------------------------------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - storage("TMP"."PERIOD_ID"=201103)

    filter("TMP"."PERIOD_ID"=201103)

    If select 201103 data, Optimizer estimates maxium 1 row need to

    process because statistic indicates no 201103 data

    Optimizer, which uses nested loop to join those tables, is correct

    according the table statistic & histogram

    Only table statistic & histogram is incorrect

  • Why accurate statistic is so important?

    What is wrong??? How it happens???

    Step 1 : Gather table statistic on today 1:00pm

    Only have 201104 data in table

    B_GTS_CST_CLASSIFY_TMP

    Step 2 : Execute ONE PASS at night 11:00pm

    Delete all data in BL_GTS_CST_CLASSIFY_TMP and then

    insert 201103 data

    Step 3 : Execute the same SQL

    Bad performance due to incorrect statistic bad joining method

  • Why accurate statistic is so important?

    Case study S2:

    Lets see what happen after gather statistic

    Gather statistic for tables using auto sample size, for all columns size autoexec dbms_stats.gather_table_stats(ownname=>BLREP',tabname=>'

    BL_GTS_CST_CLASSIFY_TMP',estimate_percent=>dbms_stats.auto_sample_size, degree=>32,

    cascade=>True, method_opt=>'FOR ALL COLUMNS SIZE AUTO');

    exec

    Check the DBA_TABLES & DBA_TAB_COLUMNS statistic information If data_type = NUMBER, SELECT UTL_RAW.CAST_TO_NUMBER(low_value),

    UTL_RAW.CAST_TO_NUMBER(HIGH_VALUE)

    FROM DBA_TAB_COLUMNS WHERE TABLE_NAME = 'ODS_PROJ_CON_REG_RELATION' AND

    COLUMN_NAME = 'PERIOD_ID'

    If data_type = VARCHAR2, SELECT UTL_RAW.CAST_TO_VARCHAR2(low_value), UTL_RAW.CAST_TO_VARCHAR2 (HIGH_VALUE)

    FROM DBA_TAB_COLUMNS WHERE TABLE_NAME = 'ODS_PROJ_CON_REG_RELATION' AND

    COLUMN_NAME = 'PERIOD_ID'

  • Why accurate statistic is so important?

    Case study S2:

    -----------------------------------------------------------------------

    | Id | Operation | Name | E-Rows |

    -----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | |

    | 1 | HASH JOIN OUTER | | 30K |

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 30K |

    |* 3 | INDEX RANGE SCAN | BL_PROJECT_CUST_DIM_N1 | 196K |

    -----------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - storage(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    filter(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    3 - access("TMP"."SUB_PROJECT_ID"="PCD"."PROJECT_ID")

    Estimate rows become 30K

    Optimizer uses HASH JOIN, execution plan changed

  • Nested Loops VS Hash Join

    When to use Hash joins

    At least one side of the join is returning many rows

    Low cardinality indexes are not available on the join keys.

    The join predicates use only equals (=) conditions.

  • Nested Loops VS Hash Join

    Nested Loops join is acceptable in a high volume SQL

    are:

    When the driving (inner) table will return 0 or 1 row. If you have a join query where one of the tables is supplied with the

    whole of a primary or unique key, Oracle can retrieve the row

    (if there is one) and then perform a full table scan on the

    second table. This is more efficient than either a sort-merge

    or a hash join.

    When the outer (second) table is very small (ie. fewer than 100 rows) and can fit into a single block. Since a single block

    is the smallest amount of data Oracle can read, a Table that

    fits into a single block can be accessed very fast with a Full

    Table Scan.

  • Bad SQL execution plan caused by incorrect statistic

    Elapsed time with correct statistic : 6min (201104)

    Elapsed time with incorrect statistic : 129min (201103)

    Elapsed time after gather statistic: 6min (201103)

  • - Recommended Methodology

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    Incorrect statistic cause Optimizer :

    Choose wrong INDEX

    Choose wrong JOIN method

  • - Recommended Methodology

    For this case,

    Step 1 : Gather table statistic on today 1:00pm

    Only have 201104 data in table

    B_GTS_CST_CLASSIFY_TMP

    Step 2 : Execute ONE PASS at night 11:00pm

    Delete all data in BL_GTS_CST_CLASSIFY_TMP and then

    insert 201103 data

    Step 3 : Execute the same SQL

    Bad performance due to incorrect statistic > bad joining method

  • - Recommended Methodology

    Solution 1:

    Gather statistic on dynamic working table after bulk

    insert / delete

    In the package logic / MOIA job:

    1. Delete table BL_GTS_CST_CLASSIFY_TMP

    2. Insert record to BL_GTS_CST_CLASSIFY_TMP

    3. Add Gather table statistic -BL_GTS_CST_CLASSIFY_TMP

    4. Run other jobs

  • - Recommended MethodologySolution 2 :

    -----------------------------------------------------------------------

    | Id | Operation | Name | E-Rows |

    -----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | |

    | 1 | HASH JOIN OUTER | | 30K |

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 30K |

    |* 3 | INDEX RANGE SCAN | BL_PROJECT_CUST_DIM_N1 | 196K |

    -----------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - storage(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    filter(("TMP"."PERIOD_ID"=:B1 AND

    INTERNAL_FUNCTION("EXPENSE_TYPE") AND NVL("TMP"."RMB_AMOUNT",0)0))

    3 - access("TMP"."SUB_PROJECT_ID"="PCD"."PROJECT_ID")

    If I know this should be the correct execution plan

  • - Recommended MethodologySolution 2 :

    Use SPM (SQL PLAN MANAGEMENT) for a statement,

    subsequent executions of that statement will use the

    SQL plan baseline.

    Example

    1. Create a SPM for a correct execution plan

    2. Next month when select data 201105, even high /

    low value statistic only have 201104, as long as

    the SQL is the SAME, the SQL will use the correct

    SQL execution plan

  • - Recommended MethodologySolution 2 :

    1. Create a SPM for a correct execution plan-----------------------------------------------------------------------

    | Id | Operation | Name | E-Rows |

    -----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | |

    | 1 | HASH JOIN OUTER | | 30K |

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 30K |

    |* 3 | INDEX RANGE SCAN | BL_PROJECT_CUST_DIM_N1 | 196K |

    -----------------------------------------------------------------------

    SQL_ID=0wbgug6xbr7gg PLAN_HASH_VALUE=656489123

    SQLPLUS> VARIABLE CNT NUMBER ;

    SQLPLUS> execute :CNT

    :=DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(SQL_ID =>

    0wbgug6xbr7gg, PLAN_HASH_VALUE => 656489123) ;

    Check the added SPM

    SQLPLUS> SELECT * FROM DBA_SQL_PLAN_BASELINES ORDER BY

    LAST_MODIFIED

  • - Recommended MethodologySolution 2 :

    1. Create a SPM for a correct execution plan

    Check the added SPM:

    SQLPLUS> SELECT SQL_HANDLE, PLAN_NAME, LAST_MODIFIED,

    ENABLED, ACCEPTED, SQL_TEX FROM DBA_SQL_PLAN_BASELINES

    ORDER BY LAST_MODIFIED

    Check the SPM execution plan:

    SQLPLUS> SELECT * FROM

    TABLE(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(SQL_HANDLE)

  • - Recommended MethodologySolution 2 :

    2. Next month when select data 201105, even high /

    low value statistic only have 201104, as long as

    the SQL is the SAME, the SQL will use the correct

    SQL execution planPLAN_TABLE_OUTPUT

    ----------------------------------------------------------------------------------------

    SQL_ID 0wbgug6xbr7gg, child number 0

    -------------------------------------

    SELECT 830 RECORD_TYPE_ID, TMP.PERIOD_ID, TMP.COMPANY_ID,

    TMP.COMPANY_CODE, TMP.FIN_REGION_ID, TMP.REGION_CODE, TMP.ACCOUNT_ID,

    TMP.ACCOUNT_CODE, TMP.PHYSICAL_PRODUCT_ID, TMP.PHYSICAL_PRODUCT_CODE,

    TMP.SERVICE_PRODUCT_ID, TMP.SERVICE_PRODUCT_CODE, PCD.CUSTOMER_ID

    CUSTOMER_ID, TMP.CUSTOMER_CODE, TMP.PROJECT_ID, TMP.PROJECT_CODE,

    TMP.COST_CENTER_ID, TMP.CURRENCY_ID, TMP.FIN_CONTRACT_ID,

    TMP.CONTRACT_NUMBER, TMP.SUB_PROJECT_ID, TMP.SUB_PROJECT_CODE,

    TMP.EXPENSE_TYPE, TMP.SOURCE_TYPE, TMP.ATTRIBUTE1, TMP.ATTRIBUTE2,

    TMP.ATTRIBUTE3, TMP.ATTRIBUTE4, TMP.ATTRIBUTE5, TMP.ATTRIBUTE6,

    TMP.ATTRIBUTE7, TMP.SER_ASSIGN_STEP, TMP.PHY_ASSIGN_STEP,

    TMP.REPORT_TYPE_FLAG, TMP.INDUSTRY_CLASS_ID, NVL(TMP.AMOUNT, 0) AMOUNT,

    NVL(TMP.FUNC_AMOUNT, 0) FUNC_AMOUNT, NVL(TMP.RMB_AMOUNT, 0) RMB_AMOUNT

    FROM BL_GTS_CST_CLASSIFY_TMP TMP, BL_PROJECT_CUST_DIM PCD WHERE

    EXPENSE_TYPE IN ('C610', 'C630') AND TMP.PERIOD_ID = :B1 AND

    NVL(TMP.RMB_AMOUNT,0) 0 AND TMP.SUB_PROJECT_ID = PCD.PROJECT_ID(+)

  • - Recommended MethodologySolution 2 :

    2. Next month when select data 201105, even high /

    low value statistic only have 201104, as long as

    the SQL is the SAME, the SQL will use the correct

    SQL execution plan-----------------------------------------------------------------------

    | Id | Operation | Name | E-Rows |

    -----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | |

    | 1 | HASH JOIN OUTER | | 30K |

    |* 2 | TABLE ACCESS STORAGE FULL| BL_GTS_CST_CLASSIFY_TMP | 30K |

    |* 3 | INDEX RANGE SCAN | BL_PROJECT_CUST_DIM_N1 | 196K |

    -----------------------------------------------------------------------

    Note

    -----

    - SQL plan baseline SYS_SQL_PLAN_fcc170b0a62d0f4d used for this statement\

    The same execution plan will use.

    If SPM is used, a note will indicate it when you

    view the execution plan

  • - Recommended Methodology

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    YES, go to next step

  • - Recommended Methodology

    Most common SQL problems that are easy to identify and easy

    to fix by below steps.

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    2. Does the SQL already have optimized joining method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove / add it and re-test

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or remove INDEX() or similar

    hints

    4. Does SQL has appropriate join predicates?

    A. If yes, go to next step

    B. If no, adding appropriate HINT

    5. For low volume SQLs, are there any Full Table/Partition Scans?

    A. If yes, check if appropriate to add index

    B. If no, go to next step

  • - Recommended Methodology

    2. Does the SQL already have optimized joining

    method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove /

    add it and re-test

  • Case study J1

    Problematic SQL: SELECT COT.CONTRACT_NUMBER ,COT.ITEM_CODE ,COT.QUANTITY ,

    Q_L.QUOTATION_ITEM_CODE ,Q_L.QUOTATION_QUANTITY FROM (SELECT /*+INDEX(DT

    ODS_CP_ORDER_OM_DAILY_TEMP_N1)*/ DT.CONTRACT_NUMBER, DT.ITEM_CODE,

    SUM(DT.QUANTITY) QUANTITY FROM ODS_CP_ORDER_OM_DAILY_TEMP DT GROUP BY

    DT.CONTRACT_NUMBER, DT.ITEM_CODE) COT, (SELECT /*+USE_NL(QH,QL)

    LEADING(QH) INDEX(QH ODS_CP_QUOTATION_HEADERS_U1) INDEX(QL

    ODS_CP_QUOTATION_LINES_N1)*/ QH.CONTRACT_NUMBER,

    QL.QUOTATION_ITEM_CODE, SUM(QL.QUOTATION_QUANTITY) QUOTATION_QUANTITY

    FROM ODS_CP_QUOTATION_HEADERS QH, ODS_CP_QUOTATION_LINES QL, (SELECT

    DT.CONTRACT_NUMBER, DT.ITEM_CODE, SUM(DT.QUANTITY) QUANTITY FROM

    ODS_CP_ORDER_OM_DAILY_TEMP DT GROUP BY DT.CONTRACT_NUMBER,

    DT.ITEM_CODE) ODT WHERE QH.QUOTATION_HEADER_ID =

    QL.QUOTATION_HEADER_ID AND ODT.CONTRACT_NUMBER = QH.CONTRACT_NUMBER

    AND ODT.ITEM_CODE = QL.QUOTATION_ITEM_CODE GROUP BY

    QH.CONTRACT_NUMBER, QL.QUOTATION_ITEM_CODE) Q_L WHERE

    COT.CONTRACT_NUMBER = Q_L.CONTRACT_NUMBER(+) AND COT.ITEM_CODE =

    Q_L.QUOTATION_ITEM_CODE(+)

  • Case study J1

    Problematic SQL: SELECT COT.CONTRACT_NUMBER ,COT.ITEM_CODE ,COT.QUANTITY ,

    Q_L.QUOTATION_ITEM_CODE ,Q_L.QUOTATION_QUANTITY FROM (SELECT /*+INDEX(DT

    ODS_CP_ORDER_OM_DAILY_TEMP_N1)*/ DT.CONTRACT_NUMBER, DT.ITEM_CODE,

    SUM(DT.QUANTITY) QUANTITY FROM ODS_CP_ORDER_OM_DAILY_TEMP DT GROUP BY

    DT.CONTRACT_NUMBER, DT.ITEM_CODE) COT, (SELECT /*+USE_NL(QH,QL)

    LEADING(QH) INDEX(QH ODS_CP_QUOTATION_HEADERS_U1) INDEX(QL

    ODS_CP_QUOTATION_LINES_N1)*/ QH.CONTRACT_NUMBER,

    QL.QUOTATION_ITEM_CODE, SUM(QL.QUOTATION_QUANTITY) QUOTATION_QUANTITY

    FROM ODS_CP_QUOTATION_HEADERS QH, ODS_CP_QUOTATION_LINES QL, (SELECT

    DT.CONTRACT_NUMBER, DT.ITEM_CODE, SUM(DT.QUANTITY) QUANTITY FROM

    ODS_CP_ORDER_OM_DAILY_TEMP DT GROUP BY DT.CONTRACT_NUMBER,

    DT.ITEM_CODE) ODT WHERE QH.QUOTATION_HEADER_ID =

    QL.QUOTATION_HEADER_ID AND ODT.CONTRACT_NUMBER = QH.CONTRACT_NUMBER

    AND ODT.ITEM_CODE = QL.QUOTATION_ITEM_CODE GROUP BY

    QH.CONTRACT_NUMBER, QL.QUOTATION_ITEM_CODE) Q_L WHERE

    COT.CONTRACT_NUMBER = Q_L.CONTRACT_NUMBER(+) AND COT.ITEM_CODE =

    Q_L.QUOTATION_ITEM_CODE(+)

  • If Nested Loop is used in this case

    -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    ......

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    .....

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    If nested loop is used for ODS_CP_QUOTATION_HEADERS

    (rows 10M) and ODS_CP_QUOTATION_LINES (rows 180M)

    ..

    14604 rows selected.

    Elapsed: more than 2hr

  • Case study J1

    If removed all HINTs and let Optimizer chooses: SELECT COT.CONTRACT_NUMBER ,COT.ITEM_CODE ,COT.QUANTITY ,

    Q_L.QUOTATION_ITEM_CODE ,Q_L.QUOTATION_QUANTITY FROM (SELECT /*+INDEX(DT

    ODS_CP_ORDER_OM_DAILY_TEMP_N1)*/ DT.CONTRACT_NUMBER, DT.ITEM_CODE,

    SUM(DT.QUANTITY) QUANTITY FROM ODS_CP_ORDER_OM_DAILY_TEMP DT GROUP BY

    DT.CONTRACT_NUMBER, DT.ITEM_CODE) COT, (SELECT /*+USE_NL(QH,QL)

    LEADING(QH) INDEX(QH ODS_CP_QUOTATION_HEADERS_U1) INDEX(QL

    ODS_CP_QUOTATION_LINES_N1)*/ QH.CONTRACT_NUMBER,

    QL.QUOTATION_ITEM_CODE, SUM(QL.QUOTATION_QUANTITY) QUOTATION_QUANTITY

    FROM ODS_CP_QUOTATION_HEADERS QH, ODS_CP_QUOTATION_LINES QL, (SELECT

    DT.CONTRACT_NUMBER, DT.ITEM_CODE, SUM(DT.QUANTITY) QUANTITY FROM

    ODS_CP_ORDER_OM_DAILY_TEMP DT GROUP BY DT.CONTRACT_NUMBER,

    DT.ITEM_CODE) ODT WHERE QH.QUOTATION_HEADER_ID =

    QL.QUOTATION_HEADER_ID AND ODT.CONTRACT_NUMBER = QH.CONTRACT_NUMBER

    AND ODT.ITEM_CODE = QL.QUOTATION_ITEM_CODE GROUP BY

    QH.CONTRACT_NUMBER, QL.QUOTATION_ITEM_CODE) Q_L WHERE

    COT.CONTRACT_NUMBER = Q_L.CONTRACT_NUMBER(+) AND COT.ITEM_CODE =

    Q_L.QUOTATION_ITEM_CODE(+)

  • Case study J1 : Checking -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:20:21.00 | 957K| 2383K|

    |* 1 | HASH JOIN OUTER | | 1 | 99751 | 14604 |00:20:21.00 | 957K| 2383K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    | 6 | HASH GROUP BY | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    |* 7 | HASH JOIN | | 1 | 46M| 541K|00:09:55.83 | 945K| 2383K|

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    | 10 | VIEW | VW_GBC_5 | 1 | 46M| 559M|00:20:02.72 | 628K| 2368K|

    | 11 | HASH GROUP BY | | 1 | 46M| 559M|00:18:31.21 | 628K| 2368K|

    |* 12 | HASH JOIN | | 1 | 46M| 627M|00:02:08.95 | 628K| 615K|

    | 13 | VIEW | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    -------------------------------------------------------------------------------------------------------------------------------------

    Check if E-Rows close to A-Rows

    If yes, go to next step

    If no, gather statistics and re-test

    Are the underlying tables and indexes analyzed? YES

  • Case study J1 : Checking -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:20:21.00 | 957K| 2383K|

    |* 1 | HASH JOIN OUTER | | 1 | 99751 | 14604 |00:20:21.00 | 957K| 2383K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    | 6 | HASH GROUP BY | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    |* 7 | HASH JOIN | | 1 | 46M| 541K|00:09:55.83 | 945K| 2383K|

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    | 10 | VIEW | VW_GBC_5 | 1 | 46M| 559M|00:20:02.72 | 628K| 2368K|

    | 11 | HASH GROUP BY | | 1 | 46M| 559M|00:18:31.21 | 628K| 2368K|

    |* 12 | HASH JOIN | | 1 | 46M| 627M|00:02:08.95 | 628K| 615K|

    | 13 | VIEW | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    -------------------------------------------------------------------------------------------------------------------------------------

    Does the SQL already have optimized joining method?

  • Case study J1 : Checking -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:20:21.00 | 957K| 2383K|

    |* 1 | HASH JOIN OUTER | | 1 | 99751 | 14604 |00:20:21.00 | 957K| 2383K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    | 6 | HASH GROUP BY | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    |* 7 | HASH JOIN | | 1 | 46M| 541K|00:09:55.83 | 945K| 2383K|

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    | 10 | VIEW | VW_GBC_5 | 1 | 46M| 559M|00:20:02.72 | 628K| 2368K|

    | 11 | HASH GROUP BY | | 1 | 46M| 559M|00:18:31.21 | 628K| 2368K|

    |* 12 | HASH JOIN | | 1 | 46M| 627M|00:02:08.95 | 628K| 615K|

    | 13 | VIEW | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    -------------------------------------------------------------------------------------------------------------------------------------

    Because the A-Rows (390K, 10M, 180M) are big numbers, hash join is themost appropriate join

    Does the SQL already have optimized joining method? YES

  • Case study J1

    If using the correct joining method

    14604 rows selected.

    Elapsed: 00:20:21.85

  • Nested Loops VS Hash Join

    Is Hash Join better than Nested Loop?

    Example:SELECT SD.SALESREP_ID,SD.SALESREP_CODE,SD.SALESREP_DESCRIPTION,

    OE.CUST_PO_NUMBER

    FROM

    ODS_OE_ORDER_HEADERS_ALL OE, BL_FIN_SALESREP_DIM SD

    WHERE OE.SALESREP_ID = SD.SALESREP_ID

    AND SD.SOURCE_CODE_ID = 1

    AND OE.FLOW_STATUS_CODE 'CANCELLED'

    AND NVL(OE.PURGE_DELETE_FLAG,'N') = 'N' AND OE.SALESREP_ID IS NOT NULL

    AND OE.CUST_PO_NUMBER = '0006821002170H' AND ROWNUM = 1

  • Case J2 Nested LoopsElapsed: 00:00:00.00

    ---------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    ---------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 7 | 4 |

    |* 1 | COUNT STOPKEY | | 1 | | 1 |00:00:00.01 | 7 | 4 |

    | 2 | NESTED LOOPS | | 1 | | 1 |00:00:00.01 | 7 | 4 |

    | 3 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 6 | 4 |

    |* 4 | TABLE ACCESS BY INDEX ROWID| ODS_OE_ORDER_HEADERS_ALL | 1 | 2 | 1 |00:00:00.01 | 4 | 4 |

    |* 5 | INDEX RANGE SCAN | ODS_OE_ORDER_HEADERS_ALL_N4 | 1 | 2 | 1 |00:00:00.01 | 3 | 3 |

    |* 6 | INDEX UNIQUE SCAN | BL_FIN_SALESREP_DIM_U1 | 1 | 1 | 1 |00:00:00.01 | 2 | 0 |

    | 7 | TABLE ACCESS BY INDEX ROWID | BL_FIN_SALESREP_DIM | 1 | 1 | 1 |00:00:00.01 | 1 | 0 |

    ---------------------------------------------------------------------------------------------------------------------------------

    Are the underlying tables and indexes analyzed? YES

    Does the SQL already have optimized joining method? YES, those result set are small, 1row for ODS_OE_ORDER_HEADERS_ALL & 1 row for BL_FIN_SALESREP_DIM

  • Nested Loops VS Hash Join

    What if using Hash join?

    Example :SELECT /*+ USE_HASH(OE SD) */

    SD.SALESREP_ID,SD.SALESREP_CODE,SD.SALESREP_DESCRIPTION,

    OE.CUST_PO_NUMBER

    FROM

    ODS_OE_ORDER_HEADERS_ALL OE, BL_FIN_SALESREP_DIM SD

    WHERE OE.SALESREP_ID = SD.SALESREP_ID

    AND SD.SOURCE_CODE_ID = 1

    AND OE.FLOW_STATUS_CODE 'CANCELLED'

    AND NVL(OE.PURGE_DELETE_FLAG,'N') = 'N' AND OE.SALESREP_ID IS NOT NULL

    AND OE.CUST_PO_NUMBER = '0006821002170H' AND ROWNUM = 1

  • Case J2 Hash JoinElapsed: 00:00:00.00

    ----------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem |

    ----------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 68 | |

    |* 1 | COUNT STOPKEY | | 1 | | 1 |00:00:00.01 | 68 | |

    |* 2 | HASH JOIN | | 1 | 2 | 1 |00:00:00.01 | 68 | 841K|

    |* 3 | TABLE ACCESS BY INDEX ROWID | ODS_OE_ORDER_HEADERS_ALL | 1 | 5 | 5 |00:00:00.01 | 8 | |

    |* 4 | INDEX RANGE SCAN | ODS_OE_ORDER_HEADERS_ALL_N4 | 1 | 5 | 5 |00:00:00.01 | 3 | |

    |* 5 | TABLE ACCESS STORAGE FULL FIRST ROWS| BL_FIN_SALESREP_DIM | 1 | 1591 | 3314 |00:00:00.01 | 60 | |

    ----------------------------------------------------------------------------------------------------------------------------------------

    Estimate rows and actual rows almost the same

    Same Elapsed time

  • Nested Loops VS Hash Join

    NESTED LOOPS HASH JOIN

    Elapsed Time: 00:00:00.01 00:00:00.01

    Buffers Get: 7 68

    NESTED LOOPS has lower Buffers Get in small result set joining

    During SQL tuning, objective :

    Reduce logical reads / buffer gets

    Most reliable metric

    Reduce CPU time

    Optimizer generates execution plans based on the 2 metrics

    Given a specific plan, CPU time and Buffer Gets wont change. But other metrics such as Elapsed time, Physical reads could change

  • - Recommended Methodology

    2. Does the SQL already have optimized joining

    method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove /

    add it and re-test

    YES, go to next step

  • - Recommended Methodology

    Most common SQL problems that are easy to identify and easy

    to fix by below steps.

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    2. Does the SQL already have optimized joining method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove / add it and re-test

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or remove INDEX() or similar

    hints

    4. Does SQL has appropriate join predicates?

    A. If yes, go to next step

    B. If no, adding appropriate HINT

    5. For low volume SQLs, are there any Full Table/Partition Scans?

    A. If yes, check if appropriate to add index

    B. If no, go to next step

  • - Recommended Methodology

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or

    remove INDEX() or similar hints

  • Exadata Overview - Hardware Architecture

    Database Grid Storage Server

    InfiniBand Network

    Redundant 40Gb/s switches

    Unified server & storage network

    14 High-performance low-cost storage servers

    8 Dual-processor x64 database servers

    OR

    2 Eight-processor x64 database servers

    100 TB High Performance disk, or336 TB High Capacity disk

    5.3 TB PCI Flash

    Data mirrored across storage servers

  • Exadata Features

    Exadata Smart Scans

    10X or greater reduction in data sent to database servers

    Exadata Storage Indexes

    Eliminate unnecessary I/Os

    Hybrid Columnar Compression

    Efficient compression increases effective storage capacity and increases user data scan bandwidths by a factor of up to 10X

    Exadata Smart Flash Cache

    Breaks random I/O bottleneck by increasing IOPs by up to 20X

    Doubles user data scan bandwidths

    I/O Resource Manager (IORM)

    Enables storage grid by prioritizing I/Os to ensure predictable performance

  • Smart IO what?

    Smart IO is not Block IO

    Block IO - data is shipped to the location where it can be

    processed - RDBMS

    Smart IO

    Some of the processing is shipped to where data resides Exadata Storage Server

    Results from the storage layer may be further processed in the

    RDBMS

  • Smart IO why? ( for Performance)

    Reduced network IO

    Data get filtered due to smart IO operations offloaded to the

    storage layer

    Reduces the processing burden on the host

    Horizontal parallelism

    Concurrent processing of the smart IO requests by many

    exadata storage servers

    Concurrent processing of smart IO requests, from a single

    database process, by many threads within a single exadata

    storage server

    Vertical (pipeline) parallelism

    Exadata storage servers processing more results while

    database is consuming results already returned

  • Smart IO How?

    Smart IO implementation is distributed across both RDBMS and

    Exadata storage server(s)

    RDBMS implements smart IO applications and may choose to use

    smart IO as opposed to block IO

    RDBMS drives smart IO

    Exadata storage server serves smart IO

  • Smart Scan Pre-requisite

    There must be a full scan on an object FTS (TABLE ACCESS STORAGE FULL)

    INDEX_FFS (INDEX STORAGE FAST FULL SCAN)

    BITMAP INDEX SCAN (BITMAP INDEX STORAGE FAST FULL SCAN)

    The scan must use Oracles Direct Path Read mechanism Mechanism changed in 11g favoring Exadata

    If a table smaller than _small_table_threshold, the table will still be cached in SGA though PARALLEL is used with PARALLEL_DEGREE_POLICY=MANUAL

    _small_table_threshold = 400M default

    The object must be stored on Oracles Exadata Storage

    Smart Scan showed in execution plan doesnt mean its really using Smart Scan

  • Explain plan table scan no exadata

    -----------------------------------------------------------------------------------

    | Id | Operation | Name |

    -----------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | |

    | * 1 | HASH JOIN | |

    | * 2 | HASH JOIN | |

    | * 3 | TABLE ACCESS FULL | SALES |

    | * 4 | TABLE ACCESS FULL | SALES |

    | * 5 | TABLE ACCESS FULL | SALES |

    ------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ------------------------------------------------------------------------------------

    1 - access("T"."CUST_ID"="T2"."CUST_ID" AND "T1"."PROD_ID"="T2"."PROD_ID" AND "T1"."CUST_ID"="T2"."CUST_ID")

    2 - access("T"."PROD_ID"="T1"."PROD_ID")

    3 - filter("T1"."PROD_ID"10000 AND "T1"."PROD_ID"45)

    4 - filter("T"."PROD_ID"

  • Explain plan table scan - exadata

    -----------------------------------------------------------------------------------

    | Id | Operation | Name |

    -----------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | |

    | * 1 | HASH JOIN | |

    | * 2 | HASH JOIN | |

    | * 3 | TABLE ACCESS STORAGE FULL | SALES |

    | * 4 | TABLE ACCESS STORAGE FULL | SALES |

    | * 5 | TABLE ACCESS STORAGE FULL | SALES |

    ------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ------------------------------------------------------------------------------------

    1 - access("T"."CUST_ID"="T2"."CUST_ID" AND "T1"."PROD_ID"="T2"."PROD_ID" AND "T1"."CUST_ID"="T2"."CUST_ID")

    2 - access("T"."PROD_ID"="T1"."PROD_ID")

    3 - storage("T1"."PROD_ID"10000 AND "T1"."PROD_ID"45)

    filter("T1"."PROD_ID"10000 AND "T1"."PROD_ID"45)

    4 - storage("T"."PROD_ID"

  • Case study J1 -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:20:21.00 | 957K| 2383K|

    |* 1 | HASH JOIN OUTER | | 1 | 99751 | 14604 |00:20:21.00 | 957K| 2383K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    | 6 | HASH GROUP BY | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    |* 7 | HASH JOIN | | 1 | 46M| 541K|00:09:55.83 | 945K| 2383K|

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    | 10 | VIEW | VW_GBC_5 | 1 | 46M| 559M|00:20:02.72 | 628K| 2368K|

    | 11 | HASH GROUP BY | | 1 | 46M| 559M|00:18:31.21 | 628K| 2368K|

    |* 12 | HASH JOIN | | 1 | 46M| 627M|00:02:08.95 | 628K| 615K|

    | 13 | VIEW | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    -------------------------------------------------------------------------------------------------------------------------------------

    The INDEX FULL SCAN prevents a Smart Scan on the table

  • Smart Scan Pre-requisite

    There must be a full scan on an object FTS (TABLE ACCESS STORAGE FULL)

    INDEX_FFS (INDEX STORAGE FAST FULL SCAN)

    BITMAP INDEX SCAN (BITMAP INDEX STORAGE FAST FULL SCAN)

    The scan must use Oracles Direct Path Read mechanism

    Mechanism changed in 11g favoring Exadata

    If a table smaller than _small_table_threshold, the table will still be cached in SGA

    _small_table_threshold = 400M default

  • Case study J1

    Try performing a full table scan instead and compare

    the performance.

    If you have an INDEX() hint, remove it.

    If you have an RULE hint, remove it.

    Add a FULL hint / INVISIBLE index to force a full table scan.

  • Case study J1 -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:20:21.00 | 957K| 2383K|

    |* 1 | HASH JOIN OUTER | | 1 | 99751 | 14604 |00:20:21.00 | 957K| 2383K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    | 6 | HASH GROUP BY | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    |* 7 | HASH JOIN | | 1 | 46M| 541K|00:09:55.83 | 945K| 2383K|

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    | 10 | VIEW | VW_GBC_5 | 1 | 46M| 559M|00:20:02.72 | 628K| 2368K|

    | 11 | HASH GROUP BY | | 1 | 46M| 559M|00:18:31.21 | 628K| 2368K|

    |* 12 | HASH JOIN | | 1 | 46M| 627M|00:02:08.95 | 628K| 615K|

    | 13 | VIEW | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    -------------------------------------------------------------------------------------------------------------------------------------

    The INDEX FULL SCAN prevents a Smart Scan on the table

    ALTER INDEX ODS_CP_QUOTATION_HEADERS_U1 INVISIABLE

  • Candidate Indexes to Invisible for Smart Scan

    Candidate Indexes to Invisible:

    INDEX RANGE SCAN - Oracle is reading 0 or more contiguous rows

    from the index.

    INDEX FULL SCAN - Oracle is reading all rows from the index, and may

    be accessing these rows in the underlying table.

    INDEX SKIP SCAN - Oracle is reading 0 or more rows from different

    parts of the index, and may be accessing these rows in the underlying

    table.

    Generally Dont Invisible:

    UNIQUE INDEX UNIQUE SCAN - Oracle is reading 0 or 1 rows from

    the index.

    INDEX FAST FULL SCAN - Oracle is reading all rows from the index,

    and is not accessing these rows in the underlying table. ie. The index

    contains all columns required to resolve the query without having to

    lookup the table.

  • Before and After removing the index() hint

    ------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    ------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:03:32.09 | 740K| 615K|

    |* 1 | HASH JOIN OUTER | | 1 | 14604 | 14604 |00:03:32.09 | 740K| 615K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 25723 | 12200 |00:03:31.94 | 728K| 615K|

    | 6 | HASH GROUP BY | | 1 | 25723 | 12200 |00:03:31.94 | 728K| 615K|

    |* 7 | HASH JOIN | | 1 | 25723 | 548K|00:03:32.07 | 728K| 615K|

    | 8 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:01.44 | 99833 | 0 |

    |* 9 | HASH JOIN | | 1 | 46M| 627M|00:02:03.78 | 628K| 615K|

    | 10 | VIEW | VW_GBF_14 | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 11 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 12 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 13 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:20.74 | 615K| 615K|

    ------------------------------------------------------------------------------------------------------------------------------------

    -------------------------------------------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |

    -------------------------------------------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | | 14604 |00:20:21.00 | 957K| 2383K|

    |* 1 | HASH JOIN OUTER | | 1 | 99751 | 14604 |00:20:21.00 | 957K| 2383K|

    | 2 | VIEW | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 3 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.14 | 12708 | 0 |

    | 4 | TABLE ACCESS STORAGE FULL | ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.11 | 12708 | 0 |

    | 5 | VIEW | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    | 6 | HASH GROUP BY | | 1 | 2665K| 12200 |00:20:20.85 | 945K| 2383K|

    |* 7 | HASH JOIN | | 1 | 46M| 541K|00:09:55.83 | 945K| 2383K|

    | 8 | TABLE ACCESS BY INDEX ROWID | ODS_CP_QUOTATION_HEADERS | 1 | 10M| 10M|00:00:05.51 | 316K| 0 |

    | 9 | INDEX FULL SCAN | ODS_CP_QUOTATION_HEADERS_U1 | 1 | 10M| 10M|00:00:01.33 | 11771 | 0 |

    | 10 | VIEW | VW_GBC_5 | 1 | 46M| 559M|00:20:02.72 | 628K| 2368K|

    | 11 | HASH GROUP BY | | 1 | 46M| 559M|00:18:31.21 | 628K| 2368K|

    |* 12 | HASH JOIN | | 1 | 46M| 627M|00:02:08.95 | 628K| 615K|

    | 13 | VIEW | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 14 | HASH GROUP BY | | 1 | 14604 | 14604 |00:00:00.13 | 12708 | 0 |

    | 15 | TABLE ACCESS STORAGE FULL| ODS_CP_ORDER_OM_DAILY_TEMP | 1 | 390K| 390K|00:00:00.10 | 12708 | 0 |

    | 16 | TABLE ACCESS STORAGE FULL | ODS_CP_QUOTATION_LINES | 1 | 180M| 180M|00:00:22.25 | 615K| 615K|

    -------------------------------------------------------------------------------------------------------------------------------------

  • Exadata - SmartScan

    Before with good joining method:

    14604 rows selected.

    Elapsed: 00:20:21.85

    After using smart scan:

    14604 rows selected.

    Elapsed: 00:03:32.90

  • Verify Smart Scan

    10046 event traceElapsed times include waiting on following events:

    Event waited on Times Max. Wait Total Waited

    ---------------------------------------- Waited ---------- ------------

    SQL*Net message to client 976 0.00 0.00

    SQL*Net message from client 976 256.36 304.17

    asynch descriptor resize 13 0.00 0.00

    gc cr multi block request 51 0.00 0.00

    cell multiblock physical read 51 0.00 0.17

    gc cr grant 2-way 18 0.00 0.00

    cell single block physical read 18 0.00 0.00

    reliable message 1 0.00 0.00

    enq: KO - fast object checkpoint 2 0.00 0.00

    cell smart table scan 728 0.00 0.19

  • Verify Smart Scan

    EXPLAIN PLAN / DBMS_XPLAN package

    Doesnt tell if Smart Scan really happen or not

    10046 Trace cell smart table scan

    cell smart index scan

    V$SESSTAT / V$MYSTAT cell scans

    V$SQL Offload Eligible Bytes

    IO_CELL_OFFLOAD_ELIGIBLE_BYTES

    IO_INTERCONNECT_BYTES

    DBMS_SQLTUNE.REPORT_SQL_MONITOR MONITOR hint

  • - Recommended Methodology

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or

    remove INDEX() or similar hints

    YES, go to next step

    Many Oracle developers - usually those working on OLTPsystems - are told early in their careers that Full Table

    Scans are bad. Many will then hold on to this prejudice

    and never learn the truth.

  • - Recommended Methodology

    Most common SQL problems that are easy to identify and easy

    to fix by below steps.

    1. Are the underlying tables and indexes analyzed?

    A. If yes, go to next step

    B. If no, gather stats and re-test

    2. Does the SQL already have optimized joining method?

    A. If yes, go to next step

    B. If no, check if inappropriate hint, if yes, remove / add it and re-test

    3. Is the SQL using smart scan?

    A. If yes, go to next step

    B. If no, invisible indexes or add FULL() hint or remove INDEX() or similar

    hints

    4. Does SQL has appropriate join predicates?

    A. If yes, go to next step

    B. If no, adding appropriate HINT

    5. For low volume SQLs, are there any Full Table/Partition Scans?

    A. If yes, check if appropriate to add index

    B. If no, go to next step

  • - Recommended Methodology

    4. Does SQL has appropriate join predicates?

    A. If yes, go to next step

    B. If no, adding appropriate HINT

  • Case Study H1

    SQL Text

    ------------------------------

    select /*+ FULL(ODS_OM_MTL_TRANSACTIONS) FULL() SQLID=4rf90vv0dbf8r */

    t.transaction_id, t.transaction_date, t.organization_id, p.segment1 company_code,

    p.segment3 account_code, p.segment4 product_code, p.segment6 region_code,

    p.segment7 to_ic, r.segment1 item_code, s.subinventory_code, s.trx_source_line_id,

    tt.description, q.transaction_type_name transaction_type, s.currency_code,

    t.primary_quantity, t.base_transaction_value from ods_mtl_transaction_accounts t,

    ods_mtl_material_transactions s,

    ods_mtl_system_items_b r, ods_mtl_transaction_types q, ods_gl_code_combinations p,

    ods_oe_order_lines_all ol, ods_oe_order_headers_all oh, ods_oe_transaction_types_tl

    tt where tt.description like '%EMS%' and t.transaction_id = s.transaction_id and

    t.organization_id = s.organization_id and s.transaction_type_id =

    q.transaction_type_id and t.inventory_item_id = r.inventory_item_id and

    t.organization_id = r.organization_id and t.reference_account =

    p.code_combination_id and s.trx_source_line_id =

    ol.line_id and ol.header_id = oh.header_id and oh.order_type_id =

    tt.transaction_type_id -- and p.segment3 not in ('1260100', '1260200') and

    s.transaction_type_id in (33, 15) and t.transaction_date >= to_date('2011-01-01',

    'YYYY-MM-DD') and t.transaction_date < to_date('2011-02-01', 'YYYY-MM-DD') and

    t.organization_id = 17221

    This SQL is using Smartscan but can I speed it up?

  • Case Study H1 Execution plan

    SQL Plan Monitoring Details (Plan Hash Value=1317483436)

    ==========================================================================================================================================================================================================================

    | Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |

    | | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | | (%) | (# samples) |

    ==========================================================================================================================================================================================================================

    | 0 | SELECT STATEMENT | | | | | | 1 | | | | | | |

    | 1 | NESTED LOOPS | | | | | | 1 | | | | | | |

    | 2 | NESTED LOOPS | | 15 | 181K | | | 1 | | | | | | |

    | 3 | HASH JOIN | | 15 | 181K | 17 | +6 | 1 | 0 | | | 4M | | |

    | 4 | TABLE ACCESS BY INDEX ROWID | ODS_MTL_SYSTEM_ITEMS_B | 5940 | 3931 | 22 | +1 | 1 | 107K | 19758 | 309MB | | 9.09 | Cpu (3) |

    | | | | | | | | | | | | | | cell single block physical read (17) |

    | 5 | INDEX RANGE SCAN | ODS_MTL_SYSTEM_ITEMS_B_N3 | 5940 | 36 | 17 | +6 | 1 | 107K | 615 | 10MB | | 0.45 | cell single block physical read (1) |

    | 6 | NESTED LOOPS | | | | | | 1 | | | | | | |

    | 7 | NESTED LOOPS | | 7510 | 177K | | | 1 | | | | | | |

    | 8 | HASH JOIN | | 7467 | 148K | 1 | +22 | 1 | 0 | | | 1M | | |

    | 9 | INDEX STORAGE FAST FULL SCAN | ODS_OE_TRANSACTION_TYPES_TL_N1 | 117 | 4 | 1 | +22 | 1 | 15 | | | | | |

    | 10 | HASH JOIN | | 56419 | 148K | 199 | +22 | 1 | 0 | | | 157M | 0.45 | Cpu (1) |

    | -> 11 | NESTED LOOPS | | | | 199 | +22 | 1 | 2M | | | | | |

    | 12 | NESTED LOOPS | | 56419 | 128K | 199 | +22 | 1 | 2M | | | | 0.45 | Cpu (1) |

    | -> 13 | NESTED LOOPS | | 56419 | 15041 | 199 | +22 | 1 | 2M | | | | | |

    | 14 | INLIST ITERATOR | | | | 1 | +22 | 1 | 1 | | | | | |

    | 15 | TABLE ACCESS BY INDEX ROWID | ODS_MTL_TRANSACTION_TYPES | 2 | 2 | 25 | +22 | 2 | 1 | | | | | |

    | -> 16 | INDEX UNIQUE SCAN | IDX_ODS_MTL_TRANS_TYPES_U1 | 2 | 1 | 199 | +22 | 2 | 2 | | | | | |

    | -> 17 | TABLE ACCESS BY INDEX ROWID | ODS_OM_MTL_TRANSACTIONS | 28210 | 14660 | 200 | +22 | 2

    | | | | | | | | | | | | | | | cell single block physical read (116) |

    | 18 | INDEX RANGE SCAN | ODS_OM_MTL_TRANSACTIONS_N13 | 53721 | 379 | 199 | +22 | 2 | 2M | 4148 | 65MB | | 1.36 | Cpu (2) |

    | | | | | | | | | | | | | | cell single block physical read (1) |

    | 19 | INDEX UNIQUE SCAN | ODS_OE_ORDER_LINES_ALL_U1 | 1 | 1 | 199 | +22 | 2M | 2M | 2M | 218K | 3GB | | 61.82 | Cpu (20) |

    85602 | 1GB | | 5.91 | Cpu (8) |

    | | | | | | | | | | | | | | cell list of blocks physical read (5) |

    | 20 | TABLE ACCESS BY INDEX ROWID | ODS_OE_ORDER_LINES_ALL | 1 | 2 | 199 | +22 | 3M | 2M | 414K | 6GB | | 20.45 | Cpu (12) |

    | | | | | | | | | | | | | | cell list of blocks physical read (31) |

    | | | | | | | | | | | | | | cell single block physical read (2) |

    | 21 | VIEW | index$_join$_007 | 3M | 17539 | | | | | | | | | |

    | 22 | HASH JOIN | | | | | | | | | | | | |

    | 23 | INDEX STORAGE FAST FULL SCAN | ODS_OE_ORDER_HEADERS_ALL_N3 | 3M | 4123 | | | | | | | | | |

    | 24 | INDEX STORAGE FAST FULL SCAN | ODS_OE_ORDER_HEADERS_ALL_N9 | 3M | 9185 | | | | | | | | | |

    | 25 | INDEX RANGE SCAN | ODS_MTL_TRANSACTION_ACCOUNT_N2 | 4 | 3 | | | | | | | | | |

    | 26 | TABLE ACCESS BY GLOBAL INDEX ROWID | ODS_MTL_TRANSACTION_ACCOUNTS | 1 | 4 | | | | | | | | | |

    | 27 | INDEX UNIQUE SCAN | ODS_GL_CODE_COMBINATIONS_U1 | 1 | 1 | | | | | | | | | |

    | 28 | TABLE ACCESS BY INDEX ROWID | ODS_GL_CODE_COMBINATIONS | 1 | 2 | | | | | | | | | |

    ==========================================================================================================================================================================================================================

    Table ods_oe_order_headers_all was executed 3M times.

  • Why inappropriate execution plan in Case Study

    H1?

    Oracle's Cost Based Optimizer works by analyzing

    several of the possible execution paths for a SQL and

    choosing the one that it considers best. For instance,

    a two table join could drive off table A and lookup

    table B for each row returned, or it could drive off

    table B. By adding in the possibilities of join methods

    and index selection, the number of possible execution

    paths increases.

  • Why inappropriate execution plan in Case Study

    H1?

    2 tables : table A & table B

    Select * from A, B where A.a=B.a

    1. Driving table A -> table B

    Or

    2. Driving table B -> table A

  • Why inappropriate execution plan in Case Study

    H1?

    3 tables : table A & table B & table C

    Select * from A, B, C where

    A.a=B.a and B.b=C.b

    1. Driving (table A -> table B result set) -> table C

    2. Driving (table B -> table A result set) -> table C

    3. Driving (table B -> table C result set) -> table A

    4. Driving (table C -> table B result set) -> table A

    5. Driving table A -> (table B -> table C result set)

    6. Driving table C -> (table B -> table A result set)

  • Why inappropriate execution plan in Case Study

    H1?

    A three table join has three times as many

    alternatives, a four table join has four times the

    alternatives of a three table join. In general, the

    number of possible execution paths for a join

    statement is proportional to n! (ie. n x n-1 x n-2 x ... x

    2 x 1), where n is the number of tables in the join.

    No. of tables

    No. of possible

    execution plan

  • Why inappropriate execution plan in Case Study

    H1?

    The problem of choosing the absolute best execution

    path becomes near impossible as n increases.

    Mathematicians call this an np-hard - or non-

    polynomial - problem.

  • Why inappropriate execution plan in Case Study

    H1?

    If you have a table join (ie. a FROM clause) with five

    or more tables, and you have not included a hint for

    join order (eg. ORDERED or LEADING ), then Oracle

    may be joining the tables in the wrong order.

  • How to fix it

    If the tables are being joined in the wrong order, you

    can supply a hint to suggest a better order.

    If Oracle is just starting with the wrong table, try a

    LEADING hint to suggest the best table to start with.

    SQLs with equi-joins will often get the rest of the joins

    right if only they know where to start.

    For ultimate control, update the FROM clause to list

    the tables in the exact order that they should be

    joined, and specify the ORDERED hint.

  • Solution for Case Study H1

    SQL Text

    ------------------------------

    select /*+ FULL(S) LEADING(ol) full(ol) full(oh) LEADING(t) FULL(t) FULL(p)

    SQLID=4rf90vv0dbf8r */ t.transaction_id, t.transaction_date, t.organization_id,

    p.segment1 company_code, p.segment3 account_code, p.segment4 product_code,

    p.segment6 region_code, p.segment7 to_ic, r.segment1 item_code,

    s.subinventory_code, s.trx_source_line_id, tt.description, q.transaction_type_name

    transaction_type, s.currency_code, t.primary_quantity, t.base_transaction_value

    from ods_mtl_transaction_accounts t,

    ods_mtl_material_transactions s, ods_mtl_system_items_b r,

    ods_mtl_transaction_types q, ods_gl_code_combinations p, ods_oe_order_lines_all ol,

    ods_oe_order_headers_all oh, ods_oe_transaction_types_tl tt where tt.description

    like '%EMS%' and t.transaction_id = s.transaction_id and t.orga