A few things about the Optimizer Thomas Kyte

Slides:



Advertisements
Similar presentations
Tuning Oracle SQL The Basics of Efficient SQLThe Basics of Efficient SQL Common Sense Indexing The Optimizer –Making SQL Efficient Finding Problem Queries.
Advertisements

Tuning: overview Rewrite SQL (Leccotech)Leccotech Create Index Redefine Main memory structures (SGA in Oracle) Change the Block Size Materialized Views,
Presented By Akin S Walter-Johnson Ms Principal PeerLabs, Inc
Introduction to SQL Tuning Brown Bag Three essential concepts.
SQL Tuning Briefing Null is not equal to null but null is null.
M ODULE 4 D ATABASE T UNING Section 3 Application Performance 1 ITEC 450 Fall 2012.
Overview of performance tuning strategies Oracle Performance Tuning Allan Young June 2008.
What Happens when a SQL statement is issued?
1 Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
Agenda Overview of the optimizer How SQL is executed Identifying statements that need tuning Explain Plan Modifying the plan.
David Konopnicki Choosing Access Path ä The basic methods. ä The access paths and when they are available. ä How the optimizer chooses among the.
1 The Optimizer How ORACLE optimizes SQL statements David Konopnicky 1997, Revised by Mordo Shalom 2004.
Optimization Exercises. Question 1 How do you think the following query should be computed? What indexes would you suggest to use? SELECT E.ename, D.mgr.
ORACLE ONLINE TRAINING Contact our Support Team : SOFTNSOL India: Skype id : softnsoltrainings id:
Relational Database Performance CSCI 6442 Copyright 2013, David C. Roberts, all rights reserved.
Executing Explain Plans and Explaining Execution Plans Craig Martin 01/20/2011.
1 Copyright © 2012, Oracle and/or its affiliates. All rights reserved. SQL Tuning tips and tricks.
Lecture 8 Index Organized Tables Clusters Index compression
All About Binds Thomas Kyte. All About Binds It’s.
Oracle Database Administration Lecture 6 Indexes, Optimizer, Hints.
Physical Database Design & Performance. Optimizing for Query Performance For DBs with high retrieval traffic as compared to maintenance traffic, optimizing.
Optimizer Yin and Yang Thomas Kyte
Oracle Database Administration Lecture 3  Transactions  SQL Language: Additional information  SQL Language: Analytic Functions.
Ashwani Roy Understanding Graphical Execution Plans Level 200.
The Model Clause explained Tony Hasler, UKOUG Birmingham 2012 Tony Hasler, Anvil Computer Services Ltd.
Michał Kwiatek – CERN /IT-DES Performance Optimization and Tuning 1 Data Management and Database Technologies Performance Optimization and Tuning Avoid.
1 Chapter 7 Optimizing the Optimizer. 2 The Oracle Optimizer is… About query optimization Is a sophisticated set of algorithms Choosing the fastest approach.
Oracle PL/SQL Practices. Critical elements of PL/SQL Best Practices Build your development toolbox Unit test PL/SQL programs Optimize SQL in PL/SQL programs.
1 What’s Up with dbms_stats? Terry Sutton Database Specialists, Inc.
Deleting LOTS of Data Author: Randy Cunningham, OCP.
Getting the Right Cardinality Thomas Kyte
11-1 Improve response time of interactive programs. Improve batch throughput. To ensure scalability of applications load vs. performance. Reduce system.
1 Theory, Practice & Methodology of Relational Database Design and Programming Copyright © Ellis Cohen Basic SQL These slides are licensed under.
Mark Inman U.S. Navy (Naval Sea Logistics Center) Session #213 Analytic SQL for Beginners.
Thomas Kyte The Best Way… Thomas Kyte What we know, shapes how we do things...
SQL Performance and Optimization l SQL Overview l Performance Tuning Process l SQL-Tuning –EXPLAIN PLANs –Tuning Tools –Optimizing Table Scans –Optimizing.
Oracle tuning: a tutorial Saikat Chakraborty. Introduction In this session we will try to learn how to write optimized SQL statements in Oracle 8i We.
Module 4 Database SQL Tuning Section 3 Application Performance.
SQL Tuning 101 excerpt: Explain Plan A Logical Approach Michael Ruckdaschel Affinion Group International.
Oracle Database Performance Secrets Finally Revealed Greg Rahn & Michael Hallas Oracle Real-World Performance Group Server Technologies.
SQL/Lesson 7/Slide 1 of 32 Implementing Indexes Objectives In this lesson, you will learn to: * Create a clustered index * Create a nonclustered index.
1 Chapter 13 Parallel SQL. 2 Understanding Parallel SQL Enables a SQL statement to be: – Split into multiple threads – Each thread processed simultaneously.
Partition Architecture Yeon JongHeum
J.NemecAre Your Statistics Bad Enough?1 Verify the effectiveness of gathering optimizer statistics Jaromir D.B. Nemec UKOUG
Chapter 5 Index and Clustering
9-1 © Prentice Hall, 2007 Topic 9: Physical Database Design Object-Oriented Systems Analysis and Design Joey F. George, Dinesh Batra, Joseph S. Valacich,
10g Tuning Highlights Presenter JEREMY SCHNEIDER Senior Consultant, ITC Technology Services.
Indexes- What?  Optional structures associated with tables  Provides a quick access path to table data  You can create indexes on one or more columns.
1 Chapter 9 Tuning Table Access. 2 Overview Improve performance of access to single table Explain access methods – Full Table Scan – Index – Partition-level.
DB Tuning : Chapter 10. Optimizer Center for E-Business Technology Seoul National University Seoul, Korea 이상근 Intelligent Database Systems Lab School of.
Optimization and Administartion of a Database Management Systems Krystian Zieja.
Table Structures and Indexing. The concept of indexing If you were asked to search for the name “Adam Wilbert” in a phonebook, you would go directly to.
8 Copyright © 2005, Oracle. All rights reserved. Gathering Statistics.
How to kill SQL Server Performance Håkan Winther.
Same Plan Different Performance Mauro Pagano. Consultant/Developer/Analyst Oracle  Enkitec  Accenture DBPerf and SQL Tuning Training Tools (SQLT, SQLd360,
LAB: Web-scale Data Management on a Cloud Lab 11. Query Execution Plan 2011/05/27.
 Reviewing basic architecture concepts  Oracle 10g Architecture  Main features of 9i and 10g
How is data stored? ● Table and index Data are stored in blocks(aka Page). ● All IO is done at least one block at a time. ● Typical block size is 8Kb.
Tuning Oracle SQL The Basics of Efficient SQL Common Sense Indexing
Oracle – SQL 10g.
SQL Implementation & Administration
Scaling SQL with different approaches
Finding more space for your tight environment
Choosing Access Path The basic methods.
Oracle Statistics by Example
JULIE McLAIN-HARPER LINKEDIN: JM HARPER
What’s Up with dbms_stats?
Understanding Indexes
Four Rules For Columnstore Query Performance
Metadata Matters Originally by : Thomas Kyte
Presentation transcript:

A few things about the Optimizer Thomas Kyte

Understanding Access Paths for the RBO Using the RBO, the optimizer chooses an execution plan based on the access paths available and the ranks of these access paths. Oracle's ranking of the access paths is heuristic. If there is more than one way to execute a SQL statement, then the RBO always uses the operation with the lower rank. Usually, operations of lower rank execute faster than those associated with constructs of higher rank. The list shows access paths and their ranking: RBO Path 9: Single-Column Indexes RBO Path 10: Bounded Range Search on Indexed Columns RBO Path 11: Unbounded Range Search on Indexed Columns RBO Path 12: Sort Merge Join RBO Path 13: MAX or MIN of Indexed Column RBO Path 14: ORDER BY on Indexed Column RBO Path 15: Full Table Scan RBO Path 1: Single Row by Rowid RBO Path 2: Single Row by Cluster Join RBO Path 3: Single Row by Hash Cluster Key with Unique or Primary Key RBO Path 4: Single Row by Unique or Primary Key RBO Path 5: Clustered Join RBO Path 6: Hash Cluster Key RBO Path 7: Indexed Cluster Key RBO Path 8: Composite Index

Datatypes

4 Using the Wrong Datatype Datatypes are constraints Optimizer uses constraints Database uses constraints (31-feb-2010) If you use the wrong data type you will –Lose data integrity –Confuse the optimizer –Spend a lot of CPU converting things so you can use builtin features

5 Wrong Datatypes ops$tkyte%ORA11GR2> create table t 2 as 3 select object_name, object_type, owner, 4 to_date( '01-jan-2007', 'dd-mon-yyyy' ) + rownum DT, 5 to_char( 6 to_date( '01-jan-2007', 'dd-mon-yyyy' ) + rownum, 7 'YYYYMMDD' 8 ) STR, 9 to_number( 10 to_char( 11 to_date( '01-jan-2007', 'dd-mon-yyyy' ) + rownum, 12 'YYYYMMDD' ) 13 ) NUM 14 from all_objects a 15 order by dbms_random.random 16 / Table created.

6 Wrong Datatypes ops$tkyte%ORA11GR2> create index dt_idx on t(dt); Index created. ops$tkyte%ORA11GR2> create index str_idx on t(str); Index created. ops$tkyte%ORA11GR2> create index num_idx on t(num); Index created.

7 Wrong Datatypes ops$tkyte%ORA11GR2> select count(object_type) from t 2 where dt = trunc(sysdate); COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select count(object_type) from t 2 where str = to_char(trunc(sysdate),'YYYYMMDD'); COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select count(object_type) from t 2 where num = to_number(to_char(trunc(sysdate),'YYYYMMDD')); COUNT(OBJECT_TYPE) “seed” dbms_stats if necessary…

8 Wrong Datatypes ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats(user,'T'); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select column_name, histogram 2 from user_tab_columns 3 where table_name = 'T'; COLUMN_NAME HISTOGRAM OBJECT_NAME NONE OBJECT_TYPE NONE OWNER NONE DT NONE STR NONE NUM NONE 6 rows selected.

9 Wrong Datatypes ops$tkyte%ORA11GR2> select count(object_type) from t 2 where str between ' ' and ' '; COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | 192 (100)| | | 1 | SORT AGGREGATE | | 1 | 18 | | | |* 2 | TABLE ACCESS FULL| T | 327 | 5886 | 192 (1)| 00:00:03 | Predicate Information (identified by operation id): filter(("STR" =' '))

10 Wrong Datatypes ops$tkyte%ORA11GR2> select count(object_type) from t 2 where num between and ; COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | 192 (100)| | | 1 | SORT AGGREGATE | | 1 | 15 | | | |* 2 | TABLE ACCESS FULL| T | 327 | 4905 | 192 (1)| 00:00:03 | Predicate Information (identified by operation id): filter(("NUM" = ))

11 Wrong Datatypes ops$tkyte%ORA11GR2> select count(object_type) from t 2 where dt between to_date( '31-dec-2013', 'dd-mon-yyyy' ) 3 and to_date( '01-jan-2014', 'dd-mon-yyyy' ); COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Tim | 0 | SELECT STATEMENT | | | | 6 (100)| | 1 | SORT AGGREGATE | | 1 | 17 | | | 2 | TABLE ACCESS BY INDEX ROWID| T | 3 | 51 | 6 (0)| 00: |* 3 | INDEX RANGE SCAN | DT_IDX | 3 | | 2 (0)| 00: Predicate Information (identified by operation id): access("DT">=TO_DATE(' :00:00', 'syyyy-mm-dd hh24:mi:ss') AND "DT"<=TO_DATE(' :00:00', 'syyyy-mm-dd hh24:mi:ss'

12 Wrong Datatypes ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats(user,'T', method_opt=>'for all indexed columns'); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> pause ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> select column_name, histogram 2 from user_tab_columns 3 where table_name = 'T'; COLUMN_NAME HISTOGRAM OBJECT_NAME NONE OBJECT_TYPE NONE OWNER NONE DT HEIGHT BALANCED STR HEIGHT BALANCED NUM HEIGHT BALANCED 6 rows selected.

13 Wrong Datatypes ops$tkyte%ORA11GR2> select count(object_type) from t 2 where str between ' ' and ' '; COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | 192 (100)| | | 1 | SORT AGGREGATE | | 1 | 18 | | | |* 2 | TABLE ACCESS FULL| T | 327 | 5886 | 192 (1)| 00:00:03 | Predicate Information (identified by operation id): filter(("STR" =' '))

14 Wrong Datatypes ops$tkyte%ORA11GR2> select count(object_type) from t 2 where dt between to_date( '31-dec-2013', 'dd-mon-yyyy' ) 3 and to_date( '01-jan-2014', 'dd-mon-yyyy' ); COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Tim | 0 | SELECT STATEMENT | | | | 6 (100)| | 1 | SORT AGGREGATE | | 1 | 17 | | | 2 | TABLE ACCESS BY INDEX ROWID| T | 3 | 51 | 6 (0)| 00: |* 3 | INDEX RANGE SCAN | DT_IDX | 3 | | 2 (0)| 00: Predicate Information (identified by operation id): access("DT">=TO_DATE(' :00:00', 'syyyy-mm-dd hh24:mi:ss') AND "DT"<=TO_DATE(' :00:00', 'syyyy-mm-dd hh24:mi:ss'

Data Patterns Affect How Things Are Done

16 Knowledge of your data is a mandatory prerequisite to understanding what is happening and why it is happening

Clustering Factor ops$tkyte%ORA11GR2> create table organized 2 as 3 select x.* 4 from (select * from stage order by object_name) x 5 / ops$tkyte%ORA11GR2> create table disorganized 2 as 3 select x.* 4 from (select * from stage order by dbms_random.random) x 5 /

Clustering Factor ops$tkyte%ORA11GR2> create index organized_idx on organized(object_name); Index created. ops$tkyte%ORA11GR2> create index disorganized_idx on disorganized(object_name); Index created.

Clustering Factor ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'ORGANIZED', 4 estimate_percent => 100, 5 method_opt=>'for all indexed columns size 254' 6 ); 7 dbms_stats.gather_table_stats 8 ( user, 'DISORGANIZED', 9 estimate_percent => 100, 10 method_opt=>'for all indexed columns size 254' 11 ); 12 end; 13 /

Clustering Factor ops$tkyte%ORA11GR2> select table_name, blocks, num_rows, 0.05*num_rows, 0.10*num_rows from user_tables 2 where table_name like '%ORGANIZED' order by 1; TABLE_NAME BLOCKS NUM_ROWS 0.05*NUM_ROWS 0.10*NUM_ROWS DISORGANIZED ORGANIZED ops$tkyte%ORA11GR2> select table_name, index_name, clustering_factor from user_indexes 2 where table_name like '%ORGANIZED' order by 1; TABLE_NAME INDEX_NAME CLUSTERING_FACTOR DISORGANIZED DISORGANIZED_IDX ORGANIZED ORGANIZED_IDX 1036

Clustering Factor ops$tkyte%ORA11GR2> select /*+ index( organized organized_idx) */ 2 count(subobject_name) 3 from organized; COUNT(SUBOBJECT_NAME) ops$tkyte%ORA11GR2> select /*+ index( disorganized disorganized_idx) */ 2 count(subobject_name) 3 from disorganized; COUNT(SUBOBJECT_NAME)

Clustering Factor select /*+ index( organized organized_idx) */ count(subobject_name) from organized call count cpu elapsed disk query current rows Parse Execute Fetch total Row Source Operation SORT AGGREGATE (cr=1398 pr=1036 pw=0 time= us) TABLE ACCESS BY INDEX ROWID ORGANIZED (cr=1398 pr=1036 pw=0 time= us cost=1400… INDEX FULL SCAN ORGANIZED_IDX (cr=362 pr=0 pw=0 time=98362 us cost=363 … 1, = 1,036 - the clustering factor…

Clustering Factor select /*+ index( disorganized disorganized_idx) */ count(subobject_name) from disorganized call count cpu elapsed disk query current rows Parse Execute Fetch total Row Source Operation SORT AGGREGATE (cr=73089 pr=1036 pw=0 time= us) TABLE ACCESS BY INDEX ROWID DISORGANIZED (cr=73089 pr=1036 pw=0 time= us … INDEX FULL SCAN DISORGANIZED_IDX (cr=362 pr=0 pw=0 time=96421 us cost=363 … 73, = 72,727 - the clustering factor…

Clustering Factor ops$tkyte%ORA11GR2> select * from organized where object_name like 'F%'; Execution Plan Plan hash value: | Id | Operation | Name | Rows | Bytes | Cost (%CP | 0 | SELECT STATEMENT | | 149 | | 6 ( | 1 | TABLE ACCESS BY INDEX ROWID| ORGANIZED | 149 | | 6 ( |* 2 | INDEX RANGE SCAN | ORGANIZED_IDX | 149 | | 3 ( Predicate Information (identified by operation id): access("OBJECT_NAME" LIKE 'F%') filter("OBJECT_NAME" LIKE 'F%')

Clustering Factor ops$tkyte%ORA11GR2> select * from disorganized where object_name like 'F%'; Execution Plan Plan hash value: | Id | Operation | Name | Rows | Bytes | Cost ( | 0 | SELECT STATEMENT | | 149 | | 152 | 1 | TABLE ACCESS BY INDEX ROWID| DISORGANIZED | 149 | | 152 |* 2 | INDEX RANGE SCAN | DISORGANIZED_IDX | 149 | | Predicate Information (identified by operation id): access("OBJECT_NAME" LIKE 'F%') filter("OBJECT_NAME" LIKE 'F%')

Clustering Factor ops$tkyte%ORA11GR2> select * from organized where object_name like 'A%'; Execution Plan Plan hash value: | Id | Operation | Name | Rows | Bytes | Cost (%CP | 0 | SELECT STATEMENT | | 1824 | 172K| 38 ( | 1 | TABLE ACCESS BY INDEX ROWID| ORGANIZED | 1824 | 172K| 38 ( |* 2 | INDEX RANGE SCAN | ORGANIZED_IDX | 1824 | | 12 ( Predicate Information (identified by operation id): access("OBJECT_NAME" LIKE 'A%') filter("OBJECT_NAME" LIKE 'A%')

Clustering Factor ops$tkyte%ORA11GR2> select * from disorganized where object_name like 'A%'; Execution Plan Plan hash value: | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 0 | SELECT STATEMENT | | 1824 | 172K| 290 (1)| 00:00:0 |* 1 | TABLE ACCESS FULL| DISORGANIZED | 1824 | 172K| 290 (1)| 00:00: Predicate Information (identified by operation id): filter("OBJECT_NAME" LIKE 'A%')

Not understanding WHY something worked

For Example: Query runs great in test Same query runs great in production Until it doesn’t Call Support – suggest: gather stats, they seem stale You do that, query runs great Until it doesn’t Gather Stats again, query runs great Until it doesn’t Did statistics “fix” it?

+peeked_binds ops$tkyte%ORA11GR2> create table t 2 as 3 select 99 id, a.* 4 from all_objects a 5 where rownum <= 20000; Table created. ops$tkyte%ORA11GR2> update t 2 set id = 1 3 where rownum = 1; 1 row updated. ops$tkyte%ORA11GR2> create index t_idx on t(id); Index created.

+peeked_binds ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt=>'for all indexed columns size 254', 5 estimate_percent => 100, 6 cascade=>TRUE ); 7 end; 8 / PL/SQL procedure successfully completed.

+peeked_binds ops$tkyte%ORA11GR2> variable n number ops$tkyte%ORA11GR2> exec :n := 99; PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select count(object_type) 2 from t 3 where id = :n; COUNT(OBJECT_TYPE)

+peeked_binds ops$tkyte%ORA11GR2> exec :n := 1; PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select count(object_type) 2 from t 3 where id = :n; COUNT(OBJECT_TYPE)

+peeked_binds ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor(format=>'+peeked_binds')); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | 82 (100)| | | 1 | SORT AGGREGATE | | 1 | 14 | | | |* 2 | TABLE ACCESS FULL| T | | 273K| 82 (0)| 00:00:01 | Peeked Binds (identified by position): :N (NUMBER): 99 Predicate Information (identified by operation id): filter("ID"=:N)

+peeked_binds ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt=>'for all indexed columns size 254', 5 estimate_percent => 100, 6 cascade=>TRUE, NO_INVALIDATE=>FALSE ); 7 end; 8 / PL/SQL procedure successfully completed.

+peeked_binds ops$tkyte%ORA11GR2> select count(object_type) 2 from t 3 where id = :n; COUNT(OBJECT_TYPE) ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor(format=>'+peeked_binds')); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 0 | SELECT STATEMENT | | | | 2 (100)| | 1 | SORT AGGREGATE | | 1 | 14 | | | 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 14 | 2 (0)| 00:0 |* 3 | INDEX RANGE SCAN | T_IDX | 1 | | 1 (0)| 00: Peeked Binds (identified by position): :N (NUMBER): 1

Understand WHY it “worked”… This case has nothing to do with statistics and everything to do with bind variable peeking… Or cardinality feedback Or adaptive cursor sharing Or …

Sometimes the explanation just doesn’t sound right..

Explain Plan “lies” Explain plan should hardly ever be used… You have to be careful when using autotrace and related tools Never use “explain=u/p” with tkprof Avoid dbms_xplan.display, use display_cursor

Explain plan lies… ops$tkyte%ORA11GR2> create table t 2 as 3 select 99 id, to_char(object_id) str_id, a.* 4 from all_objects a 5 where rownum <= 20000; Table created. ops$tkyte%ORA11GR2> update t 2 set id = 1 3 where rownum = 1; 1 row updated. ops$tkyte%ORA11GR2> create index t_idx on t(id); Index created. ops$tkyte%ORA11GR2> create index t_idx2 on t(str_id); Index created.

Explain plan lies… ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt=>'for all indexed columns size 254', 5 estimate_percent => 100, 6 cascade=>TRUE ); 7 end; 8 / PL/SQL procedure successfully completed.

Explain plan lies… Need a volunteer

Explain plan lies… Need a volunteer select count(*) from t where id = :n; What cardinality would you estimate and why?

Explain plan lies… ops$tkyte%ORA11GR2> variable n number ops$tkyte%ORA11GR2> exec :n := 99; PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> select count(*) from t where id = :n; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | 1 | 3 | 12 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 3 | | | |* 2 | INDEX FAST FULL SCAN| T_IDX | | | 12 (0)| 00:00:01 | Predicate Information (identified by operation id): filter("ID"=TO_NUMBER(:N)) <<= a clue right here

Explain plan lies… ops$tkyte%ORA11GR2> select count(*) from t where id = 1; Execution Plan Plan hash value: | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | 1 | 3 | 1 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 3 | | | |* 2 | INDEX RANGE SCAN| T_IDX | 1 | 3 | 1 (0)| 00:00:01 | Predicate Information (identified by operation id): access("ID"=1)

Explain plan lies… ops$tkyte%ORA11GR2> select count(*) from t where id = 99; Execution Plan Plan hash value: | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | 1 | 3 | 12 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 3 | | | |* 2 | INDEX FAST FULL SCAN| T_IDX | | | 12 (0)| 00:00:01 | Predicate Information (identified by operation id): filter("ID"=99)

Explain plan lies… ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> select object_id from t where str_id = :n; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 0 | SELECT STATEMENT | | 1 | 19 | 2 (0)| 00:0 | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 19 | 2 (0)| 00:0 |* 2 | INDEX RANGE SCAN | T_IDX2 | 1 | | 1 (0)| 00: Predicate Information (identified by operation id): access("STR_ID"=:N) <<== interesting…

Explain plan lies… ops$tkyte%ORA11GR2> select object_id from t where str_id = :n; OBJECT_ID ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | 86 (100)| | |* 1 | TABLE ACCESS FULL| T | 1 | 19 | 86 (0)| 00:00:02 | Predicate Information (identified by operation id): filter(TO_NUMBER("STR_ID")=:N) <<= string has to convert..

Explain plan lies… 1 - filter(TO_NUMBER("STR_ID")=:N) <<= string has to convert.. STR_ID ,

Parallel, how did that happen

Unplanned Parallel Everything is running normal Reorganize (you know, to make things faster) over the weekend Monday rolls around and…

Unplanned Parallel ops$tkyte%ORA11GR2> create table t 2 as 3 select * 4 from all_objects; Table created. ops$tkyte%ORA11GR2> create index t_idx on t(object_id); Index created. ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' ); PL/SQL procedure successfully completed.

Unplanned Parallel ops$tkyte%ORA11GR2> select sum(object_id) 2 from t t1 3 where object_id > 5000; SUM(OBJECT_ID) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | 47 (100)| | | 1 | SORT AGGREGATE | | 1 | 5 | | | |* 2 | INDEX FAST FULL SCAN| T_IDX | | 346K| 47 (3)| 00:00:01 | Predicate Information (identified by operation id): filter("OBJECT_ID">5000)

Unplanned Parallel ops$tkyte%ORA11GR2> alter index t_idx rebuild parallel 4; Index altered.

Unplanned Parallel ops$tkyte%ORA11GR2> select sum(object_id) 2 from t t2 3 where object_id > 5000; SUM(OBJECT_ID) ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 0 | SELECT STATEMENT | | | | 47 (100)| | 1 | SORT AGGREGATE | | 1 | 5 | | | 2 | PX COORDINATOR | | | | | | 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 5 | | | 4 | SORT AGGREGATE | | 1 | 5 | | | 5 | PX BLOCK ITERATOR | | | 346K| 47 (3)| 00:0 |* 6 | INDEX FAST FULL SCAN| T_IDX | | 346K| 47 (3)| 00: Predicate Information (identified by operation id): access(:Z>=:Z AND :Z<=:Z) filter("OBJECT_ID">5000)

Unplanned Parallel ops$tkyte%ORA11GR2> select 'T', table_name, degree 2 from user_tables 3 where table_name = 'T' 4 union all 5 select 'I', index_name, degree 6 from user_indexes 7 where table_name = 'T'; ' TABLE_NAME DEGREE T T 1 I T_IDX 4

The optimizer isn’t pushy enough

Pushing Create a view Query view using a predicate Discover that the view is materialized AND THEN the predicate is applied Run query with predicate – predicate is evaluated as the query is done, resulting in far far superior performance BUT…

Pushing ops$tkyte%ORA11GR2> create table emp as select * from scott.emp; Table created. ops$tkyte%ORA11GR2> create index job_idx on emp(job); Index created. ops$tkyte%ORA11GR2> exec dbms_stats.set_table_stats ( user, 'EMP', numrows => ); PL/SQL procedure successfully completed.

Pushing ops$tkyte%ORA11GR2> create or replace view v 2 as 3 select ename, sal, job, 4 sum(sal) over (partition by job) sal_by_job, 5 sum(sal) over (partition by deptno) sal_by_deptno 6 from emp 7 / View created.

Pushing ops$tkyte%ORA11GR2> select * 2 from v 3 where job = 'CLERK' 4 order by ename 5 / ENAME SAL JOB SAL_BY_JOB SAL_BY_DEPTNO ADAMS 1100 CLERK JAMES 950 CLERK MILLER 1300 CLERK SMITH 800 CLERK | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | | | (100)| | | 1 | SORT ORDER BY | | 1000K| 49M| 65M| (1)| 00:06:37 | |* 2 | VIEW | V | 1000K| 49M| | (1)| 00:04:02 | | 3 | WINDOW SORT | | 1000K| 37M| 49M| (1)| 00:04:02 | | 4 | WINDOW SORT | | 1000K| 37M| 49M| (1)| 00:04:02 | | 5 | TABLE ACCESS FULL| EMP | 1000K| 37M| | 41 (30)| 00:00:01 | Predicate Information (identified by operation id): 2 - filter("JOB"='CLERK')

Pushing ops$tkyte%ORA11GR2> select ename, sal, job, 2 sum(sal) over (partition by job) sal_by_job, 3 sum(sal) over (partition by deptno) sal_by_deptno 4 from emp 5 where job = 'CLERK' order by ename;

Pushing ops$tkyte%ORA11GR2> select ename, sal, job, 2 sum(sal) over (partition by job) sal_by_job, 3 sum(sal) over (partition by deptno) sal_by_deptno 4 from emp 5 where job = 'CLERK' order by ename; ENAME SAL JOB SAL_BY_JOB SAL_BY_DEPTNO ADAMS 1100 CLERK JAMES 950 CLERK MILLER 1300 CLERK SMITH 800 CLERK | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| T | 0 | SELECT STATEMENT | | | | | 207 (100)| | 1 | SORT ORDER BY | | | 380K| 520K| 207 (2)| 0 | 2 | WINDOW SORT | | | 380K| 520K| 207 (2)| 0 | 3 | WINDOW BUFFER | | | 380K| | 207 (2)| 0 | 4 | TABLE ACCESS BY INDEX ROWID| EMP | | 380K| | 2 (0)| 0 |* 5 | INDEX RANGE SCAN | JOB_IDX | 14 | | | 1 (0)| Predicate Information (identified by operation id): 5 - access("JOB"='CLERK')

Pushing ops$tkyte%ORA11GR2> select ename, sal, sal_by_job 2 from v 3 where job = 'CLERK' 4 order by ename 5 / ENAME SAL SAL_BY_JOB ADAMS JAMES MILLER SMITH | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| | 0 | SELECT STATEMENT | | | | | 97 (100)| | 1 | SORT ORDER BY | | | 341K| 480K| 97 (2)| | 2 | VIEW | V | | 341K| | 2 (0)| | 3 | WINDOW BUFFER | | | 253K| | 2 (0)| | 4 | TABLE ACCESS BY INDEX ROWID| EMP | | 253K| | 2 (0)| |* 5 | INDEX RANGE SCAN | JOB_IDX | 14 | | | 1 (0)| Predicate Information (identified by operation id): 5 - access("JOB"='CLERK')

Why do we gather statistics?

“Cardinality”

“Wrong Cardinality => Wrong Plan”

“Wrong Plan => Wrong Cardinality”

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> create table t 2 as select decode( mod(rownum,2), 0, 'N', 'Y' ) flag1, 3 decode( mod(rownum,2), 0, 'Y', 'N' ) flag2, a.* 4 from all_objects a 5 / Table created. ops$tkyte%ORA11GR2> create index t_idx on t(flag1,flag2); Index created. ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt=>'for all indexed columns size 254' ); 5 end; 6 / PL/SQL procedure successfully completed.

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> select 'select * from t', num_rows 2 from user_tables where table_name = 'T' 3 union all 4 select 'select * from t where flag1 = "N"', num_rows/2 5 from user_tables where table_name = 'T' 6 union all 7 select 'select * from t where flag2 = "N"', num_rows/2 8 from user_tables where table_name = 'T' 9 union all 10 select 'select * from t where flag1 = "N" and flag2 = "N"', num_rows/2/2 11 from user_tables where table_name = 'T'; 'SELECT*FROMT' NUM_ROWS select * from t select * from t where flag1 = "N" select * from t where flag2 = "N" select * from t where flag1 = "N" and flag2 = "N"

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> select * from t where flag1='N'; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | 3635K| 301 (1)| 00:00:04 | |* 1 | TABLE ACCESS FULL| T | | 3635K| 301 (1)| 00:00:04 | Predicate Information (identified by operation id): filter("FLAG1"='N')

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> select * from t where flag2='N'; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | 3608K| 301 (1)| 00:00:04 | |* 1 | TABLE ACCESS FULL| T | | 3608K| 301 (1)| 00:00:04 | Predicate Information (identified by operation id): filter("FLAG2"='N')

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> select * from t where flag1='N' and flag2='N'; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | | 1810K| 301 (1)| 00:00:04 | |* 1 | TABLE ACCESS FULL| T | | 1810K| 301 (1)| 00:00:04 | Predicate Information (identified by operation id): filter("FLAG2"='N' AND "FLAG1"='N')

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> select /*+ gather_plan_statistics */ * 2 from t where flag1='N' and flag2='N'; no rows selected

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST')); PLAN_TABLE_OUTPUT | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | | 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.02 | 1080 | |* 1 | TABLE ACCESS FULL| T | 1 | | 0 |00:00:00.02 | 1080 | Predicate Information (identified by operation id): filter(("FLAG2"='N' AND "FLAG1"='N')) 19 rows selected.

Wrong Plan => Wrong Cardinality ops$tkyte%ORA11GR2> select /*+ dynamic_sampling(t 3) */ * from t where flag1='N' and flag2='N'; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | 0 | SELECT STATEMENT | | 6 | 612 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T | 6 | 612 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | T_IDX | 6 | | 1 (0)| 00:00:01 | Predicate Information (identified by operation id): access("FLAG1"='N' AND "FLAG2"='N') Note dynamic sampling used for this statement (level=2)

Wrong Plan => Wrong Cardinality SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),:"SYS_B_00"), NVL(SUM(C2),:"SYS_B_01"), NVL(SUM(C3),:"SYS_B_02") FROM (SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("T") FULL("T") NO_PARALLEL_INDEX("T") */ :"SYS_B_03" AS C1, CASE WHEN "T"."FLAG1"= :"SYS_B_04" AND "T"."FLAG2"=:"SYS_B_05" THEN :"SYS_B_06" ELSE :"SYS_B_07" END AS C2, CASE WHEN "T"."FLAG2"=:"SYS_B_08" AND "T"."FLAG1"=:"SYS_B_09“ THEN :"SYS_B_10" ELSE :"SYS_B_11" END AS C3 FROM "OPS$TKYTE"."T" SAMPLE BLOCK (:"SYS_B_12", :"SYS_B_13") SEED (:"SYS_B_14") "T") SAMPLESUB

X X

Programming to fail…

Too smart for their own good Parse count Parse count (failures) Quick stories about parsing…

Programming to fail… Begin execute immediate ‘begin internal_pkg.some_code; end;’; Exception when others then null; End;

Programming to fail… Sandeep - my math was wrong, I said 40% of your hard parses were failed parses. In looking at the numbers again: Statistic Total per Second per Trans parse count (failures) 389, parse count (hard) 607, parse count (total) 6,775,397 1, It would be correct to say that 64% (yes, 64%!!!!!!!!!!!!!) of your parses are *FAILED* parsed. The parse count hard included failed and successful parses - therefore, it is 389k/607k*100 to get the right percentage. 2 out of 3 SQL statements FAIL PARSING. That is sick