Presentation is loading. Please wait.

Presentation is loading. Please wait.

Optimizer Yin and Yang Thomas Kyte

Similar presentations


Presentation on theme: "Optimizer Yin and Yang Thomas Kyte"— Presentation transcript:

1 Optimizer Yin and Yang Thomas Kyte http://asktom.oracle.com/

2 Programming to fail…

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

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

5 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,176 109.0 3.0 parse count (hard) 607,096 170.1 4.7 parse count (total) 6,775,397 1,898.0 52.3 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

6 Abusing Functions

7 Function Abuse Cardinality estimation issues May reduce access paths Can increase CPU needs (repeated function calls) Could lead to partition elimination elimination

8 Cardinality Estimation Issues ops$tkyte%ORA11GR2> create table t 2 as 3 select * 4 from all_objects 5 / Table created.

9 Cardinality Estimation Issues ops$tkyte%ORA11GR2> select count(*) 2 from t 3 where created >= to_date( '5-sep-2010', 'dd-mon-yyyy' ) 4 and created < to_date( '6-sep-2010', 'dd-mon-yyyy' ) 5 / COUNT(*) ---------- 65925 ops$tkyte%ORA11GR2> select count(*), 0.01 * count(*), 0.01 * 0.01 * count(*) 2 from t 3 / COUNT(*) 0.01*COUNT(*) 0.01*0.01*COUNT(*) ---------- ------------- ------------------ 72926 729.26 7.2926

10 Cardinality Estimation Issues ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' ); PL/SQL procedure successfully completed. Why did I wait till here to gather statistics?

11 Cardinality Estimation Issues ops$tkyte%ORA11GR2> select count(*) 2 from t t2 3 where created >= to_date( '5-sep-2010', 'dd-mon-yyyy' ) 4 and created < to_date( '6-sep-2010', 'dd-mon-yyyy' ) 5 / COUNT(*) ---------- 65925 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 291 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 65462 | 511K| 291 (1)| 00:00:04 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("CREATED"<TO_DATE(' 2010-09-06 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "CREATED">=TO_DATE(' 2010-09-05 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))

12 Cardinality Estimation Issues ops$tkyte%ORA11GR2> select count(*) 2 from t t1 3 where trunc(created) = to_date( '5-sep-2010', 'dd-mon-yyyy' ) 4 / COUNT(*) ---------- 65925 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 294 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 729 | 5832 | 294 (2)| 00:00:04 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(TRUNC(INTERNAL_FUNCTION("CREATED"))=TO_DATE(' 2010-09-05 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

13 Cardinality Estimation Issues ops$tkyte%ORA11GR2> select count(*) 2 from t t1 3 where trunc(created) = to_date( '5-sep-2010', 'dd-mon-yyyy' ) 4 and substr( owner, 1, 3 ) = 'SYS' 5 / COUNT(*) ---------- 33535 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 292 (100)| | | 1 | SORT AGGREGATE | | 1 | 14 | | | |* 2 | TABLE ACCESS FULL| T | 7 | 98 | 292 (1)| 00:00:04 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter((SUBSTR("OWNER",1,3)='SYS' AND TRUNC(INTERNAL_FUNCTION("CREATED"))=TO_DATE(' 2010-09-05 00:00:00' 'syyyy-mm-dd hh24:mi:ss')))

14 Cardinality Estimation Issues ops$tkyte%ORA11GR2> select count(*) 2 from t t1 3 where trunc(created) = to_date( '5-sep-2010', 'dd-mon-yyyy' ) 4 and substr( owner, 1, 3 ) = 'SYS' 5 and mod(object_id,100000) > 1 6 / COUNT(*) ---------- 33535 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 292 (100)| | | 1 | SORT AGGREGATE | | 1 | 19 | | | |* 2 | TABLE ACCESS FULL| T | 1 | 19 | 292 (1)| 00:00:04 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter((SUBSTR("OWNER",1,3)='SYS' AND MOD("OBJECT_ID",100000)>1 AND TRUNC(INTERNAL_FUNCTION("CREATED"))=TO_DATE(' 2010-09-05 00:00 'syyyy-mm-dd hh24:mi:ss'))) 23 rows selected.

15 Copyright © 2012, Oracle and/or its affiliates. All rights reserved.Insert Information Protection Policy Classification from Slide 13 15 Compile with warnings… SQL> alter session set plsql_warnings='enable:all’; SQL> create or replace procedure p 2 as 3 begin 4 dbms_output.put_line( 'hello world' ); 5 exception 6 when others 7 then null; 8 end; 9 / Warning: Procedure created with compilation errors. c##tkyte%CDB1> show errors Errors for PROCEDURE P: LINE/COL ERROR ---- ----------------------------------------------------------------- 6/6 PLS-06009: procedure "P" OTHERS handler does not end in RAISE or RAISE_APPLICATION_ERROR

16 16 Increased CPU ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_date varchar2(30) := '01-jan-2011'; 4 l_start number := dbms_utility.get_cpu_time; 5 begin 6 for i in 1.. 10 7 loop 8 for x in ( select owner, object_name 9 from big_table.big_table 10 where created = l_date ) 11 loop 12 null; 13 end loop; 14 end loop; 15 dbms_output.put_line( 'CPU: ' || 16 to_char( dbms_utility.get_cpu_time-l_start ) ); 17 end; 18 / SP2-0804: Procedure created with compilation warnings ops$tkyte%ORA11GR2> exec p CPU: 132

17 17 Increased CPU ops$tkyte%ORA11GR2> show errors procedure p Errors for PROCEDURE P: LINE/COL ERROR -------- ----------------------------------------------------------------- 10/36 PLW-07204: conversion away from column type may result in sub-optimal query plan … 7 loop 8 for x in ( select owner, object_name 9 from big_table.big_table 10 where created = l_date ) 11 loop 12 null; 13 end loop; …

18 18 Increased CPU ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_date date := to_date('01-jan-2011','dd-mon-yyyy'); 4 l_start number := dbms_utility.get_cpu_time; 5 begin 6 for i in 1.. 10 7 loop 8 for x in ( select owner, object_name 9 from big_table.big_table 10 where created = l_date ) 11 loop 12 null; 13 end loop; 14 end loop; 15 dbms_output.put_line( 'CPU: ' || 16 to_char( dbms_utility.get_cpu_time-l_start ) ); 17 end; 18 / Procedure created. ops$tkyte%ORA11GR2> exec p CPU: 94 30% less CPU in this case

19 19 Reduced Access Paths ops$tkyte%ORA11GR2> create table t 2 ( x varchar2(20) constraint t_pk primary key, 3 y varchar2(30) 4 ); Table created. ops$tkyte%ORA11GR2> insert into t 2 select user_id, username 3 from all_users; 47 rows created. ops$tkyte%ORA11GR2> commit; Commit complete.

20 20 Reduced Access Paths ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_rec t%rowtype; 4 l_key number := 5; 5 begin 6 select * into l_rec from t where x = l_key; 7 for x in (select plan_table_output 8 from TABLE( dbms_xplan.display_cursor())) 9 loop 10 dbms_output.put_line( x.plan_table_output ); 11 end loop; 12 end; 13 / SP2-0804: Procedure created with compilation warnings

21 21 Reduced Access Paths … 5 begin 6 select * into l_rec from t where x = l_key; 7 for x in (select plan_table_output … ops$tkyte%ORA11GR2> show errors Errors for PROCEDURE P: LINE/COL ERROR -------- ----------------------------------------------------------- 6/42 PLW-07204: conversion away from column type may result in sub-optimal query plan

22 22 Reduced Access Paths ops$tkyte%ORA11GR2> exec p SQL_ID 18796jgha0hwz, child number 0 ------------------------------------- SELECT * FROM T WHERE X = :B1 Plan hash value: 1601196873 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | |* 1 | TABLE ACCESS FULL| T | 1 | 29 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(TO_NUMBER("X")=:B1)

23 23 Reduced Access Paths ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_rec t%rowtype; 4 l_key varchar2(5) := '5'; 5 begin 6 select * into l_rec from t where x = l_key; 7 for x in (select plan_table_output 8 from TABLE( dbms_xplan.display_cursor())) 9 loop 10 dbms_output.put_line( x.plan_table_output ); 11 end loop; 12 end; 13 / Procedure created. ops$tkyte%ORA11GR2> show errors No errors.

24 24 Reduced Access Paths ops$tkyte%ORA11GR2> exec p SQL_ID 18796jgha0hwz, child number 1 ------------------------------------- SELECT * FROM T WHERE X = :B1 Plan hash value: 1303508680 ------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 1 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 29 | 1 (0)| 00:00:01 | |* 2 | INDEX UNIQUE SCAN | T_PK | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("X"=:B1)

25 25 Partition Elimination Eliminated ops$tkyte%ORA11GR2> CREATE TABLE t 2 ( 3 dt date, 4 x int, 5 y varchar2(30) 6 ) 7 PARTITION BY RANGE (dt) 8 ( 9 PARTITION part1 VALUES LESS THAN(to_date('31-jan-2011', 'dd-mon-yyyy')), 10 PARTITION part2 VALUES LESS THAN(to_date('28-feb-2011', 'dd-mon-yyyy')) 11 ) 12 / Table created.

26 26 Partition Elimination Eliminated ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_date timestamp := timestamp'2011-01-15 00:00:00.000'; 4 l_count number; 5 begin 6 select count(*) into l_count from t where dt = l_date; 7 8 for x in (select plan_table_output 9 from TABLE( dbms_xplan.display_cursor() ) ) 10 loop 11 dbms_output.put_line( '.'||x.plan_table_output ); 12 end loop; 13 end; 14 / SP2-0804: Procedure created with compilation warnings

27 27 Partition Elimination Eliminated … 5 begin 6 select count(*) into l_count from t where dt = l_date; 7 … SP2-0804: Procedure created with compilation warnings ops$tkyte%ORA11GR2> show errors Errors for PROCEDURE P: LINE/COL ERROR -------- -------------------------------------------------------------- 6/47 PLW-07204: conversion away from column type may result in sub-optimal query plan

28 28 Partition Elimination Eliminated SQL_ID 0t5m83d3m67q7, child number 0 ------------------------------------- SELECT COUNT(*) FROM T WHERE DT = :B1 Plan hash value: 3225603066 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | | | 1 | SORT AGGREGATE | | 1 | 9 | | | | | | 2 | PARTITION RANGE ALL| | 1 | 9 | 2 (0)| 00:00:01 | 1 | 2 | |* 3 | TABLE ACCESS FULL | T | 1 | 9 | 2 (0)| 00:00:01 | 1 | 2 | --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - filter(INTERNAL_FUNCTION("DT")=:B1)

29 29 Partition Elimination Eliminated ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_date date := to_date( '2011-01-15', 'yyyy-mm-dd' ); 4 l_count number; 5 begin 6 select count(*) into l_count from t where dt = l_date; 7 8 for x in (select plan_table_output 9 from TABLE( dbms_xplan.display_cursor() ) ) 10 loop 11 dbms_output.put_line( '.'||x.plan_table_output ); 12 end loop; 13 end; 14 / Procedure created. ops$tkyte%ORA11GR2> show errors No errors.

30 30 Partition Elimination Eliminated.SQL_ID 0t5m83d3m67q7, child number 1.-------------------------------------.SELECT COUNT(*) FROM T WHERE DT = :B1..Plan hash value: 3660200434..------------------------------------------------------------------------------------------------.| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |.------------------------------------------------------------------------------------------------.| 0 | SELECT STATEMENT | | | | 2 (100)| | | |.| 1 | SORT AGGREGATE | | 1 | 9 | | | | |.| 2 | PARTITION RANGE SINGLE| | 1 | 9 | 2 (0)| 00:00:01 | KEY | KEY |.|* 3 | TABLE ACCESS FULL | T | 1 | 9 | 2 (0)| 00:00:01 | KEY | KEY |.------------------------------------------------------------------------------------------------..Predicate Information (identified by operation id):.---------------------------------------------------.. 3 - filter("DT"=:B1)

31 31 Partition Elimination Eliminated ops$tkyte%ORA11GR2> alter session set Plsql_Warnings = 'error:all‘; ops$tkyte%ORA11GR2> create or replace procedure p authid definer 2 as 3 l_date timestamp := timestamp'2011-01-15 00:00:00.000'; 4 l_count number; 5 begin 6 select count(*) into l_count from t where dt = l_date; 7 8 for x in (select plan_table_output 9 from TABLE( dbms_xplan.display_cursor() ) ) 10 loop 11 dbms_output.put_line( '.'||x.plan_table_output ); 12 end loop; 13 end; 14 /

32 Gathering stats when you shouldn’t

33 33 Right command, wrong time Temporary tables –Empty at 3am, full at 3pm Queue Tables –Same as above Partitioned Transactional Tables –Partition starts empty –Grows rapidly –Gather stats every hour? (no…)

34 34 Right command, wrong time – Temporary tables Temporary tables –Dynamic Sampling might be appropriate (might be shared over sessions) –Fill with representative data, Gather and Lock –Cardinality/Opt_Estimate Hints on a session by session basis –12c Session Private Statistics Hard parse…

35 35 Copies stats from source partition to destination partition Adjusts min & max values for partition column at both partition & global level Copies statistics of the dependent objects Columns, local indexes etc. Does not update global indexes Maybe use DBMS_STATS.COPY_TABLE_STATS(); Sales Table SALES_1995 : SALES_Q4_2003 SALES_Q1_2004 DBMS_STATS.COPY_TABLE_STATS (‘SH’, 'SALES’, 'SALES_Q4_2003’, 'SALES_Q1_2004’); Right command, wrong time – Partitioned tables

36 Not Having Representative Stats When You Should

37 Is 10% enough? 1,000,000 rows Grows at a rate of 10,000 rows per month 10 months – no stats… What might happen?

38 38 Is 10% right? ops$tkyte%ORA11GR2> create table t 2 as 3 select * 4 from ( 5 select add_months(sysdate,-100) + mod( rownum, 3000 ) dt 6 from dual 7 connect by level <= 1000000 8 ) 9 where dt < trunc(sysdate,'y') 10 / Table created.

39 39 Is 10% right? ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' ); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select num_rows from user_tables where table_name = 'T'; NUM_ROWS ---------- 994672

40 40 Is 10% right? ops$tkyte%ORA11GR2> insert into t 2 select trunc(sysdate,'y') + mod( rownum, 150 ) dt 3 from dual 4 connect by level <= 50000 5 / 50000 rows created. ops$tkyte%ORA11GR2> commit; Commit complete.

41 41 Is 10% right? ops$tkyte%ORA11GR2> select trunc(dt,'mm'), count(*) 2 from t 3 where dt >= add_months( trunc(sysdate,'y'),-3) 4 group by trunc(dt,'mm') order by 1; TRUNC(DT, COUNT(*) --------- ---------- 01-OCT-13 10323 01-NOV-13 9990 01-DEC-13 10323 01-JAN-14 10353 01-FEB-14 9344 01-MAR-14 10323 01-APR-14 9990 01-MAY-14 9990 8 rows selected.

42 42 Is 10% right? ops$tkyte%ORA11GR2> select count(*) 2 from t 3 where dt between to_date( '01-dec-2013' ) 4 and to_date( '31-dec-2013' ); COUNT(*) ---------- 9990 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 506 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 10517 | 84136 | 506 (2)| 00:00:07 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("DT">=TO_DATE(' 2013-12-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "DT"<=TO_DATE(' 2013-12-31 00:00:00', 'syyyy-mm-d hh24:mi:ss')))

43 43 Is 10% right? ops$tkyte%ORA11GR2> select count(*) 2 from t 3 where dt between to_date( '01-jan-2014' ) 4 and to_date( '31-jan-2014' ); COUNT(*) ---------- 10353 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 506 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 333 | 2664 | 506 (2)| 00:00:07 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("DT">=TO_DATE(' 2014-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "DT"<=TO_DATE(' 2014-01-31 00:00:00', 'syyyy-mm-d hh24:mi:ss')))

44 44 Is 10% right? ops$tkyte%ORA11GR2> select count(*) 2 from t 3 where dt between to_date( '01-sep-2014' ) 4 and to_date( '30-sep-2014' ); COUNT(*) ---------- 0 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 506 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 333 | 2664 | 506 (2)| 00:00:07 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("DT">=TO_DATE(' 2014-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "DT"<=TO_DATE(' 2014-09-30 00:00:00', 'syyyy-mm-d hh24:mi:ss')))

45 45 Is 10% right? When might be the time to gather stats? At what %? “it depends”

46 46 Is 10% right? ops$tkyte%ORA11GR2> create or replace function stats_to_date(p_raw raw) 2 return date 3 as 4 l_dt date; 5 begin 6 dbms_stats.convert_raw_value(p_raw,l_dt); 7 return l_dt; 8 end; 9 / Function created. ops$tkyte%ORA11GR2> select stats_to_date( low_value ), stats_to_date( high_value ) 2 from user_tab_columns 3 where table_name = 'T'; STATS_TO_ --------- 31-OCT-05 31-DEC-13

47 47 Is 10% right? ops$tkyte%ORA11GR2> declare 2 l_distcnt number; 3 l_density number; 4 l_nullcnt number; 5 rec_srec dbms_stats.statrec; 6 datevals dbms_stats.datearray; 7 l_avgclen number; 8 l_low date; 9 l_high date; 10 BEGIN 11 dbms_stats.get_column_stats 12 (user, 'T', 'DT', 13 distcnt => l_distcnt, 14 density => l_density, 15 nullcnt => l_nullcnt, 16 srec => rec_srec, 17 avgclen => l_avgclen ); 18 19 select stats_to_date(low_value), stats_to_date(high_value) 20 into l_low, l_high 21 from user_tab_col_statistics 22 where table_name = 'T' 23 and column_name = 'DT'; 25 l_high := add_months(l_high, 5); 26 datevals := dbms_stats.datearray (l_low, l_high); 27 rec_srec.minval:=NULL; 28 rec_srec.maxval:=NULL; 29 rec_srec.bkvals:=NULL; 30 rec_srec.novals:=NULL; 31 32 dbms_stats.prepare_column_values (rec_srec, datevals); 33 34 dbms_stats.set_column_stats 35 (user, 'T', 'DT', 36 distcnt => l_distcnt, 37 density => l_density, 38 nullcnt => l_nullcnt, 39 srec => rec_srec, 40 avgclen => l_avgclen ); 41 END; 42 /

48 48 Is 10% right? ops$tkyte%ORA11GR2> select stats_to_date( low_value ), stats_to_date( high_value ) 2 from user_tab_columns 3 where table_name = 'T'; STATS_TO_ --------- 31-OCT-05 31-MAY-14

49 49 Is 10% right? ops$tkyte%ORA11GR2> select count(*) 2 from t t2 3 where dt between to_date( '01-jan-2014' ) 4 and to_date( '31-jan-2014' ); COUNT(*) ---------- 10353 ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor); --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 506 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 10188 | 81504 | 506 (2)| 00:00:07 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("DT">=TO_DATE(' 2014-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "DT"<=TO_DATE(' 2014-01-31 00:00:00', 'syyyy-mm-d hh24:mi:ss')))

50 50 Is 10% right? Knowledge of your data is a mandatory prerequisite to developing a sensible statistics implementation

51 Hints are a bad idea (in general)

52 Hints are a bad idea Who here thinks they know how to hint? Two types of hints – Good hints – Bad hints Hints are fragile – Slightly different environments.. – Upgrades/Patches When done correctly, they are still bad (forces a plan forever if you do it right)

53 Hinting is hard ops$tkyte%ORA11GR2> create table sales as select * from sh.sales; Table created. ops$tkyte%ORA11GR2> create table customers as select * from sh.customers; Table created. ops$tkyte%ORA11GR2> create index sales_cust_bix on sales(cust_id); Index created. ops$tkyte%ORA11GR2> alter table customers add constraint customers_pk primary key(cust_id); Table altered.

54 Hinting is hard ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats ( user, 'SALES' ); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats ( user, 'CUSTOMERS' ); PL/SQL procedure successfully completed.

55 Hinting is hard ops$tkyte%ORA11GR2> select /*+ use_nl_with_index(s sales_cust_bix) */ 2 c.cust_id, c.cust_first_name, 3 c.cust_last_name, s.amount_sold 4 from customers c, sales s 5 where c.cust_id = s.cust_id 6 / Execution Plan ---------------------------------------------------------- Plan hash value: 2056508761 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Ti -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 918K| 26M| | 2690 (1)| 00 |* 1 | HASH JOIN | | 918K| 26M| 1736K| 2690 (1)| 00 | 2 | TABLE ACCESS FULL| CUSTOMERS | 55500 | 1083K| | 405 (1)| 00 | 3 | TABLE ACCESS FULL| SALES | 918K| 8973K| | 1236 (1)| 00 --------------------------------------------------------------------------------

56 Hinting is hard atom_hint=(@=0x5b1f10 err=0 resol=1 used=0 token=898 org=1 lvl=3 txt=USE_NL_WITH_INDEX ("S" "SALES_CUST_BIX") )

57 Hinting is hard ops$tkyte%ORA11GR2> select /*+ leading(c s) use_nl_with_index(s sales_cust_bix) */ 2 c.cust_id, c.cust_first_name, 3 c.cust_last_name, s.amount_sold 4 from customers c, sales s 5 where c.cust_id = s.cust_id 6 / Execution Plan ---------------------------------------------------------- Plan hash value: 4100337089 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%C -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 918K| 26M| 852K | 1 | NESTED LOOPS | | | | | 2 | NESTED LOOPS | | 918K| 26M| 852K | 3 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 1083K| 405 |* 4 | INDEX RANGE SCAN | SALES_CUST_BIX | 130 | | 2 | 5 | TABLE ACCESS BY INDEX ROWID| SALES | 17 | 170 | 107 --------------------------------------------------------------------------------

58 Hinting is hard ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor(format=>'+outline'));.. -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%C -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 852K(1 | 1 | NESTED LOOPS | | | | | 2 | NESTED LOOPS | | 918K| 26M| 852K | 3 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 1083K| 405 |* 4 | INDEX RANGE SCAN | SALES_CUST_BIX | 130 | | 2 | 5 | TABLE ACCESS BY INDEX ROWID| SALES | 17 | 170 | 107 -------------------------------------------------------------------------------- Outline Data ------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('11.2.0.2') DB_VERSION('11.2.0.3') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "C"@"SEL$1") INDEX(@"SEL$1" "S"@"SEL$1" ("SALES"."CUST_ID")) LEADING(@"SEL$1" "C"@"SEL$1" "S"@"SEL$1") USE_NL(@"SEL$1" "S"@"SEL$1") NLJ_BATCHING(@"SEL$1" "S"@"SEL$1") END_OUTLINE_DATA */

59 Hinting is bad If you can hint it, you can baseline it. If you baseline it – it will use that plan, and that plan can evolve over time (if we let it)

60 Don’t hint - baseline ops$tkyte%ORA11GR2> CREATE TABLE t AS SELECT * FROM all_objects; ops$tkyte%ORA11GR2> ALTER TABLE t ADD CONSTRAINT t_id_pk PRIMARY KEY (object_id); ops$tkyte%ORA11GR2> CREATE INDEX t_idx_type ON t(object_type); ops$tkyte%ORA11GR2> exec DBMS_STATS.GATHER_TABLE_STATS(user,'T');

61 Don’t hint - baseline ops$tkyte%ORA11GR2> SELECT * 2 FROM t t1 3 WHERE t1.object_type = 'TABLE' 4 AND t1.object_id > (SELECT MAX(t2.object_id) - 500000 FROM t t2); ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost ( ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 94 | 9118 | 32 | 1 | TABLE ACCESS BY INDEX ROWID | T | 94 | 9118 | 30 | 2 | BITMAP CONVERSION TO ROWIDS | | | | | 3 | BITMAP AND | | | | | 4 | BITMAP CONVERSION FROM ROWIDS| | | | | 5 | SORT ORDER BY | | | | |* 6 | INDEX RANGE SCAN | T_ID_PK | 1871 | | 3 | 7 | SORT AGGREGATE | | 1 | 5 | | 8 | INDEX FULL SCAN (MIN/MAX)| T_ID_PK | 1 | 5 | 2 | 9 | BITMAP CONVERSION FROM ROWIDS| | | | |* 10 | INDEX RANGE SCAN | T_IDX_TYPE | 1871 | | 6 ------------------------------------------------------------------------------

62 Don’t hint - baseline ops$tkyte%ORA11GR2> begin 2 dbms_output.put_line( 3 dbms_spm.load_plans_from_cursor_cache 4 ( sql_id => 'crvk9z6mx9n4d' ) 5 ); 6 end; 7 / 1 ops$tkyte%ORA11GR2> select sql_handle, 2 substr(sql_text,1,10)||'...'|| 3 substr(sql_text,length(sql_text)-10) stext, 4 plan_name, enabled 5 from dba_sql_plan_baselines 6 where sql_text like 7 'SELECT%FROM t t1%(SELECT MAX(t2.object_id) - 500000 FROM t t2)'; SQL_HANDLE STEXT PLAN_NAME ENA -------------------- ------------------------ ------------------------------ --- SQL_e738c19a5191e8fd SELECT * SQL_PLAN_fff61m98t3u7xda64b1bb YES... FROM t t2)

63 Don’t hint - baseline ops$tkyte%ORA11GR2> begin 2 dbms_output.put_line( 3 dbms_spm.alter_sql_plan_baseline 4 ( sql_handle => 'SQL_e738c19a5191e8fd', 5 attribute_name => 'enabled', 6 attribute_value => 'NO' ) 7 ); 8 end; 9 / 1

64 Don’t hint - baseline ops$tkyte%ORA11GR2> select sql_handle, 2 substr(sql_text,1,10)||'...'|| 3 substr(sql_text,length(sql_text)-10) stext, 4 plan_name, enabled 5 from dba_sql_plan_baselines 6 where sql_text like 7 'SELECT%FROM t t1%(SELECT MAX(t2.object_id) - 500000 FROM t t2)'; SQL_HANDLE STEXT PLAN_NAME ENA -------------------- ------------------------ ------------------------------ --- SQL_e738c19a5191e8fd SELECT * SQL_PLAN_fff61m98t3u7xda64b1bb NO... FROM t t2)

65 Don’t hint - baseline ops$tkyte%ORA11GR2> SELECT /*+ first_rows(1) */ * 2 FROM t t1 3 WHERE t1.object_type = 'TABLE' 4 AND t1.object_id > (SELECT MAX(t2.object_id) - 500000 FROM t t2); Execution Plan ---------------------------------------------------------- Plan hash value: 1289158178 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU) ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 97 | 4 (0) |* 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 97 | 2 (0) |* 2 | INDEX RANGE SCAN | T_IDX_TYPE | 1871 | | 1 (0) | 3 | SORT AGGREGATE | | 1 | 5 | | 4 | INDEX FULL SCAN (MIN/MAX)| T_ID_PK | 1 | 5 | 2 (0) ------------------------------------------------------------------------------

66 Don’t hint - baseline ops$tkyte%ORA11GR2> begin 2 dbms_output.put_line( 3 dbms_spm.load_plans_from_cursor_cache 4 ( sql_id => '5mn39tz7fpjnu', 5 plan_hash_value => 1289158178, 6 sql_handle => 'SQL_e738c19a5191e8fd' ) 7 ); 8 end; 9 / 1

67 Don’t hint - baseline ops$tkyte%ORA11GR2> select sql_handle, 2 substr(sql_text,1,10)||'...'|| 3 substr(sql_text,length(sql_text)-10) stext, 4 plan_name, enabled 5 from dba_sql_plan_baselines 6 where sql_text like 7 'SELECT%FROM t t1%(SELECT MAX(t2.object_id) - 500000 FROM t t2)'; SQL_HANDLE STEXT PLAN_NAME ENA -------------------- ------------------------ ------------------------------ --- SQL_e738c19a5191e8fd SELECT * SQL_PLAN_fff61m98t3u7x971f1a3f YES... FROM t t2) SQL_e738c19a5191e8fd SELECT * SQL_PLAN_fff61m98t3u7xda64b1bb NO... FROM t t2)

68 Don’t hint - baseline ops$tkyte%ORA11GR2> SELECT * 2 FROM t t1 3 WHERE t1.object_type = 'TABLE' 4 AND t1.object_id > (SELECT MAX(t2.object_id) - 500000 FROM t t2); ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU) ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 97 | 4 (0) |* 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 97 | 2 (0) |* 2 | INDEX RANGE SCAN | T_IDX_TYPE | 1871 | | 1 (0) | 3 | SORT AGGREGATE | | 1 | 5 | | 4 | INDEX FULL SCAN (MIN/MAX)| T_ID_PK | 1 | 5 | 2 (0) ------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("T1"."OBJECT_ID"> (SELECT /*+ PUSH_SUBQ INDEX ("T2" "T_ID_PK") * MAX("T2"."OBJECT_ID")-500000 FROM "T" "T2")) 2 - access("T1"."OBJECT_TYPE"='TABLE') Note ----- - SQL plan baseline "SQL_PLAN_fff61m98t3u7x971f1a3f" used for this statement

69 Baseline And consider this a patch – a quick fix. The real solution will be in cardinalities…

70 What Estimate Percent to Use?

71 What Estimate Percent? The two most important statistics are…

72 What Estimate Percent? The two most important statistics are… NUM_ROWS NDV

73 Copyright © 2012, Oracle and/or its affiliates. All rights reserved. 73 How to gather statistics  # 1 most commonly asked question – “What sample size should I use?”  Controlled by ESTIMATE_PRECENT parameter  From 11g onwards use default value AUTO_SAMPLE_SIZE – New hash based algorithm – Speed of a 10% sample – Accuracy of 100% sample Sample Size More info in the following paper http://dl.acm.org/citation.cfm?id=1376721http://dl.acm.org/citation.cfm?id=1376721

74 Copyright © 2012, Oracle and/or its affiliates. All rights reserved. 74 How to gather statistics  Speed of a 10% sample  Accuracy of 100% sample Sample Size Run Num AUTO_SAMPLE_SIZE 10% SAMPLE 100% SAMPLE 100:02:21.8600:02:31.5600:08:24.10 200:02:38.1100:02:49.4900:07:38.25 300:02:39.3100:02:38.5500:07:37.83 Column Name NDV with AUTO_SAMPLE_SIZE NDV with 10% SAMPLE NDV with 100% SAMPLE C1598523146460351 C212709126085441289760 C3768384359424777942

75 “X” is bad

76 Nothing is 100% true in software Conversely, nothing is 100% false Every thing, every feature, has a time and a place Never say never – Never say always I always say…… Do not disable things globally

77 select * from t1, t2 where t1. id = t2. id and t1.small_distinct = :x Is there a best way to do something – every time? T1 is large, where small_distinct = :x returns much of the table T2 is large

78 select * from t1, t2 where t1. id = t2. Id and t1.small_distinct = :x Is there a best way to do something – every time? HASH JOIN TABLE ACCESS FULL T1 TABLE ACCESS FULL T2 SELECT STATEMENT NESTED LOOPS TABLE ACCESS BY INDEX ROWID(T1) INDEX RANGE SCAN T1_IDX TABLE ACCESS BY INDEX ROWID(T2) INDEX UNIQUE SCAN T2_PK

79 Is there a best way to do something – every time? HASH JOIN TABLE ACCESS FULL T1 TABLE ACCESS FULL T2 SELECT STATEMENT NESTED LOOPS TABLE ACCESS BY INDEX ROWID(T1) INDEX RANGE SCAN T1_IDX TABLE ACCESS BY INDEX ROWID(T2) INDEX UNIQUE SCAN T2_PK call count cpu elapsed disk query Fetch 35227 5.63 9.32 23380 59350 Fetch 35227 912.07 3440.70 1154555 121367981

80 Is there a best way to do something – every time? HASH JOIN TABLE ACCESS FULL T1 TABLE ACCESS FULL T2 SELECT STATEMENT NESTED LOOPS TABLE ACCESS BY INDEX ROWID(T1) INDEX RANGE SCAN T1_IDX TABLE ACCESS BY INDEX ROWID(T2) INDEX UNIQUE SCAN T2_PK call count cpu elapsed disk query Fetch 1 4.55 5.16 12152 12456 Fetch 1 0.05 0.09 12 15

81

82

83 “Tune” this query

84 I need a volunteer…

85 Tune this query ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x); --------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 65 | 1 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 65 | | | | 2 | NESTED LOOPS | | 1 | 65 | 1 (0)| 00:00:01 | | 3 | NESTED LOOPS | | 1 | 52 | 1 (0)| 00:00:01 | | 4 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 26 | 1 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | T3_IDXON_SOME_OTHER_ID | 1 | | 1 (0)| 00:00:01 | | 6 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 26 | 0 (0)| 00:00:01 | |* 7 | INDEX RANGE SCAN | T2_IDXON_T2_ID | 1 | | 0 (0)| 00:00:01 | |* 8 | INDEX RANGE SCAN | T1_IDXON_T1_ID | 1 | 13 | 0 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------------------

86 Tune this query Impossible task given the information you have You can remove (+) and that is about it (but we already do that) So, let’s see what the developer gave us to work with… ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x);

87 Tune this query ops$tkyte%ORA11GR2> CREATE TABLE T1 2 ( 3 T1_ID NUMBER(18), 4 data varchar2(1000) 5 ); Table created. ops$tkyte%ORA11GR2> create index t1_idxon_t1_id on t1(t1_id); Index created. ops$tkyte%ORA11GR2> CREATE TABLE T2 2 ( 3 T2_ID NUMBER(18), 4 T1_ID NUMBER(18), 5 data varchar2(1000) 6 ); Table created. ops$tkyte%ORA11GR2> create index t2_idxon_t2_id on t2(t2_id); Index created.

88 Tune this query ops$tkyte%ORA11GR2> CREATE TABLE T3 2 ( 3 T3_ID NUMBER(18), 4 SOME_OTHER_ID NUMBER(18), 5 data varchar2(1000) 6 ); Table created. ops$tkyte%ORA11GR2> create index t3_idxon_t3_id on t3(t3_id); Index created. ops$tkyte%ORA11GR2> create index t3_idxon_some_other_id on t3(some_other_id); Index created.

89 Tune this query Still impossible task given the information you have You don’t know what or how the tables relate to each other – 1:1, 1:M? You don’t know if the relationships are mandatory or optional “Application enforces them” you are told – So you ask for them – primary keys, foreign keys, all constraints

90 Tune this query ops$tkyte%ORA11GR2> ALTER TABLE T1 ADD CONSTRAINT T1_PK1 PRIMARY KEY (T1_ID); Table altered. ops$tkyte%ORA11GR2> ALTER TABLE T2 2 ADD CONSTRAINT T2_PK1 3 PRIMARY KEY (T2_ID); Table altered. ops$tkyte%ORA11GR2> ALTER TABLE T3 2 ADD CONSTRAINT T3_ORDER_PK1 3 PRIMARY KEY (T3_ID); Table altered. Now we know primary keys and quite a few “NOT NULL” constraints

91 Tune this query ops$tkyte%ORA11GR2> ALTER TABLE T2 2 ADD CONSTRAINT T2_OSO_FK1 3 FOREIGN KEY (T1_ID) 4 REFERENCES T1 (T1_ID); Table altered. ops$tkyte%ORA11GR2> ALTER TABLE T3 2 ADD CONSTRAINT T3_OLS_S_FK1 3 FOREIGN KEY (T3_ID) 4 REFERENCES T2 (T2_ID); Table altered. ops$tkyte%ORA11GR2> alter table t2 2 modify t1_id not null; Table altered. Along with foreign keys – and a NOT NULL constraint on T2

92 Tune this query ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x); T1( T1_ID primary key ) T2( T2_ID primary key, T1_ID foreign key to T1 and NOT NULL ) T3( T3_ID primary key, T3_ID foreign key to T2(T2_ID) ) T1 T3 T2 t1.t1_id = t2.t1_id t2.t2_id = t3.t3_id

93 Tune this query ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x); ops$tkyte%ORA11GR2> select count(*) 2 from t3 3 where t3.some_other_id = to_number(:v0); -------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 13 | 1 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 13 | | | |* 2 | INDEX RANGE SCAN| T3_IDXON_SOME_OTHER_ID | 1 | 13 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------- These are now semantically equivalent queries. How did I get there?

94 Tune this query ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x); First, we know the outer join is not necessary –Where t2.col = t3.col(+) and t3.anything = ‘something’ –Implies the (+) is not necessary If the outer join ‘happened’, then t3.anything would be NULL! And t3.anything = to_number(:v0) would never be satisfied ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id 5 and t3.some_other_id = to_number(:x);

95 Tune this query Second, we know that T1 is not relevant to the query –Nothing is projected from T1 in the output –T1(t1_id) is the primary key, joined to T2(t1_id) – so T2 is “key preserved” –T2(t1_id) is NOT NULL and is a foreign key to T1 –Therefore, when you join T1 to T2 – every row in T2 appears at least once and at most once in the output ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x); ops$tkyte%ORA11GR2> select count(*) 2 from t2, t3 3 where t2.t2_id = t3.t3_id 5 and t3.some_other_id = to_number(:x);

96 Tune this query Lastly, we know that T2 is not relevant to the query –Nothing is projected from T2 in the output –T2(T2_ID) is the primary key, joined to T3(T3_ID) – so T3 is “key preserved” –T3(T3_ID) is NOT NULL and is a foreign key to T2 –Therefore, when you join T2 to T3 – every row in T3 appears at least once and at most once in the output ops$tkyte%ORA11GR2> select count(*) 2 from t1, t2, t3 3 where t1.t1_id = t2.t1_id 4 and t2.t2_id = t3.t3_id(+) 5 and t3.some_other_id = to_number(:x); ops$tkyte%ORA11GR2> select count(*) 2 from t3 3 where t3.some_other_id = to_number(:x);

97 Tune this query ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T3 3 WHERE T3.related_service_order_id = TO_NUMBER(:v0); Is the same as…. But only because of the constraints in place… Actually.. We could probably ‘tune’ this more…

98 Tune this query So, do your developers have to be this smart? Nope.. 10053 trace (after constraints added) shows: SQL:******* UNPARSED QUERY IS ******* SELECT COUNT(*) "COUNT(*)" FROM "OPS$TKYTE"."T1" "T1","OPS$TKYTE"."T2" "T2","OPS$TKYTE"."T3" "T3" WHERE "T2"."T1_ID"="T1"."T1_ID" AND "T2"."T2_ID"="T3"."T3_ID"(+) AND "T3"."SOME_OTHER_ID"=TO_NUMBER(:B1) JE: eliminate table: T1 (T1)... SQL:******* UNPARSED QUERY IS ******* SELECT COUNT(*) "COUNT(*)" FROM "OPS$TKYTE"."T2" "T2","OPS$TKYTE"."T3" "T3" WHERE "T2"."T2_ID"="T3"."T3_ID"(+) AND "T3"."SOME_OTHER_ID"=TO_NUMBER(:B1) Query block SEL$FFB75F5A (#0) simplified... OJE: Converting outer join of T3 and T2 to inner-join.... SQL:******* UNPARSED QUERY IS ******* SELECT COUNT(*) "COUNT(*)" FROM "OPS$TKYTE"."T2" "T2","OPS$TKYTE"."T3" "T3" WHERE "T3"."T3_ID"="T2"."T2_ID" AND "T3"."SOME_OTHER_ID"=TO_NUMBER(:B1) JE: eliminate table: T2 (T2) SQL:******* UNPARSED QUERY IS ******* SELECT COUNT(*) "COUNT(*)" FROM "OPS$TKYTE"."T3" "T3" WHERE "T3"."SOME_OTHER_ID"=TO_NUMBER(:B1)

99 Tune this query Datatypes are constraints, affect cardinality estimates Check constraints used for query rewrite NOT NULL as well And foreign key/primary key/unique constraints Dimensions are used to rewrite What about a data warehouse? What about deferrable constraints?

100 Fake Default Values

101 Fear of Nulls Use some out of range value – Which obviously changes the high/low values – Which impacts cardinality estimates Afraid of not being able to use indexes – Nulls are not indexed – NOT TRUE Could the use of fake values lead to data integrity issues?

102 Fake Values ops$tkyte%ORA11GR2> create table t 2 as 3 select * 4 from ( 5 select add_months(sysdate,-100) + mod( rownum, 3000 ) dt 6 from dual 7 connect by level <= 1000000 8 ) 9 where dt < trunc(sysdate,'y') 10 / Table created. ops$tkyte%ORA11GR2> insert into t 2 select * 3 from ( 4 select null dt 5 from dual 6 connect by level <= 1000000 7 ) 8 / 1000000 rows created.

103 Fake Values ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' ); PL/SQL procedure successfully completed.

104 Fake Values ops$tkyte%ORA11GR2> select count(*) 2 from t 3 where dt between to_date( '01-jun-2013' ) and to_date( '30-jun-2013' ); COUNT(*) ---------- 9657 Execution Plan ---------------------------------------------------------- Plan hash value: 2966233522 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 4 | 949 (2)| 00:00:12 | | 1 | SORT AGGREGATE | | 1 | 4 | | | |* 2 | TABLE ACCESS FULL| T | 10337 | 41348 | 949 (2)| 00:00:12 | ---------------------------------------------------------------------------

105 Fake Values ops$tkyte%ORA11GR2> create table t 2 as 3 select * 4 from ( 5 select add_months(sysdate,-100) + mod( rownum, 3000 ) dt 6 from dual 7 connect by level <= 1000000 8 ) 9 where dt < trunc(sysdate,'y') 10 / Table created. ops$tkyte%ORA11GR2> insert into t 2 select * 3 from ( 4 select to_date( '01-jan-9999') dt 5 from dual 6 connect by level <= 1000000 7 ) 8 / 1000000 rows created.

106 Fake Values ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' ); PL/SQL procedure successfully completed.

107 Fake Values ops$tkyte%ORA11GR2> select count(*) 2 from t 3 where dt between to_date( '01-jun-2013' ) and to_date( '30-jun-2013' ); COUNT(*) ---------- 9657 Execution Plan ---------------------------------------------------------- Plan hash value: 2966233522 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 1018 (2)| 00:00:13 | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| T | 1356 | 10848 | 1018 (2)| 00:00:13 | ---------------------------------------------------------------------------

108 Null Values ops$tkyte%ORA11GR2> create table t 2 as 3 select case when mod(rownum,1000)=0 then null else object_type end otype, 4 stage.* 5 from stage 6 / Table created. ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' ); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> create index t_idx on t(otype); Index created. ops$tkyte%ORA11GR2> analyze index t_idx validate structure; Index analyzed. ops$tkyte%ORA11GR2> select lf_rows, (select count(*) from t), 2 lf_rows- (select count(*) from t) diff 3 from index_stats; LF_ROWS (SELECTCOUNT(*)FROMT) DIFF ---------- --------------------- ---------- 79920 80000 -80

109 Null Values ops$tkyte%ORA11GR2> select * from t where otype is null; -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 80 | 3760 | 163 (1)| 00:00:02 | |* 1 | TABLE ACCESS FULL| T | 80 | 3760 | 163 (1)| 00:00:02 | -------------------------------------------------------------------------- ops$tkyte%ORA11GR2> select /*+ index( t t_idx ) */ * from t where otype is null; -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 80 | 3760 | 163 (1)| 00:00:02 | |* 1 | TABLE ACCESS FULL| T | 80 | 3760 | 163 (1)| 00:00:02 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OTYPE" IS NULL)

110 Null Values ops$tkyte%ORA11GR2> drop index t_idx; Index dropped. ops$tkyte%ORA11GR2> create index t_idx on t(otype,0); Index created.

111 Null Values ops$tkyte%ORA11GR2> select * from t where otype is null; Execution Plan ---------------------------------------------------------- Plan hash value: 470836197 ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 80 | 3760 | 5 (0)| 00:0 | 1 | TABLE ACCESS BY INDEX ROWID| T | 80 | 3760 | 5 (0)| 00:0 |* 2 | INDEX RANGE SCAN | T_IDX | 80 | | 2 (0)| 00:0 ------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OTYPE" IS NULL)

112

113 Birmingham Traffic Engineer Gregory Dawkins says the city may change the system to keep Roberson from receiving more tickets. He says "maybe we just need to leave that part blank altogether."

114

115


Download ppt "Optimizer Yin and Yang Thomas Kyte"

Similar presentations


Ads by Google