• Save
Sep13 optimizer stats
Upcoming SlideShare
Loading in...5
×
 

Sep13 optimizer stats

on

  • 996 views

OOW13

OOW13

Statistics

Views

Total Views
996
Views on SlideShare
996
Embed Views
0

Actions

Likes
1
Downloads
0
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Sep13 optimizer stats Sep13 optimizer stats Document Transcript

  • 9/26/2013 1 WARNING itty bitty fonts in this presentation SQL> exec sample_font Can you read this ? 1 Connor McDonald OracleDBA co.uk 2 www.oracledba.co.uk connormcdonald.wordpress.com @connor_mc_d
  • 9/26/2013 2 3
  • 9/26/2013 3 bio slide 5
  • 9/26/2013 4 gratuitous book plug 7 8
  • 9/26/2013 5 why ? 9
  • 9/26/2013 6 11
  • 9/26/2013 7 life was paradise 13 14
  • 9/26/2013 8 (good) cost optimizer 15 16
  • 9/26/2013 9 version 7.0 17 18 optimizer_mode = CHOOSE
  • 9/26/2013 10 "we choose RULE" 19 20 SQL> analyze table SALES estimate statistics;
  • 9/26/2013 11 21 eventually.... 22
  • 9/26/2013 12 it got better 23 added more functionality 24
  • 9/26/2013 13 25 SQL> analyze table SALES estimate statistics 2 for table 3 for columns size 10 4 for ....; SQL syntax engine 26
  • 9/26/2013 14 oracle 8i 27 DBMS_STATS 28
  • 9/26/2013 15 problem.... 29 30 analyze table SALES estimate statistics;SQL> 41 characters
  • 9/26/2013 16 31 SQL> 2 3 4 5 6 7 8 128 characters begin dbms_stats.gather_table_stats( ownname=>'HR', tabname=>'SALES', cascade=>true, estimate_percent=>20 ); end; don't get chaining 32
  • 9/26/2013 17 s l o w e r 33 "I don't think so....." 34
  • 9/26/2013 18 times have changed ... 35 36 SQL> desc DBMS_STATS FUNCTION CLOB_TO_VARRAY FUNCTION CONV_RAW FUNCTION CREATE_EXTENDED_STATS FUNCTION DIFF_TABLE_STATS_IN_HISTORY FUNCTION DIFF_TABLE_STATS_IN_PENDING FUNCTION DIFF_TABLE_STATS_IN_STATTAB FUNCTION GET_COMPATIBLE FUNCTION GET_PARAM FUNCTION GET_PREFS FUNCTION GET_STATS_HISTORY_AVAILABILITY FUNCTION GET_STATS_HISTORY_RETENTION FUNCTION GET_STAT_TAB_VERSION FUNCTION REPORT_COL_USAGE FUNCTION REPORT_GATHER_AUTO_STATS FUNCTION REPORT_GATHER_DATABASE_STATS FUNCTION REPORT_GATHER_DICTIONARY_STATS
  • 9/26/2013 19 151 routines ! 37 12c 38
  • 9/26/2013 20 use DBMS_STATS 39 features 40 profiles baselines tuning sets adaptive cursor sharing bind peeking column groups extended statistics
  • 9/26/2013 21 better stats 41 hard to convince 42
  • 9/26/2013 22 43 stop using analyze 44
  • 9/26/2013 23 ego 45 "good ol' days" 46
  • 9/26/2013 24 47 48
  • 9/26/2013 25 today.... 49 optimizer is probably... 50
  • 9/26/2013 26 ... smarter than you 51 52 SQL> select count(e.hiredate) 2 from DEPT d, EMP e 3 where e.deptno = d.deptno(+) 4 and e.sal > 10; nested loop outer sort merge outer hash hash anti nested loop anti
  • 9/26/2013 27 53 ------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| EMP | 14 | ------------------------------------------- no DEPT ? 54 SQL> select count(e.hiredate) 2 from DEPT d, EMP e 3 where e.deptno = d.deptno(+) 4 and e.sal > 10; not null foreign key key preserved
  • 9/26/2013 28 Oracle's solution.... 55 statistics by stealth... 56
  • 9/26/2013 29 10g 57 58 SQL> select owner, job_name, enabled 2 from dba_scheduler_jobs 3 where owner = 'SYS'; OWNER JOB_NAME ENABLED --------------- ------------------------------ ------- SYS PURGE_LOG TRUE SYS FGR$AUTOPURGE_JOB TRUE SYS GATHER_STATS_JOB TRUE SYS AUTO_SPACE_ADVISOR_JOB TRUE FALSE
  • 9/26/2013 30 11g 59 60 SQL> select owner, job_name, enabled 2 from dba_scheduler_jobs 3 where owner = 'SYS'; OWNER JOB_NAME ENABLED --------------- ------------------------------ ------- SYS AUTO_SPACE_ADVISOR_JOB FALSE SYS BSLN_MAINTAIN_STATS_JOB TRUE SYS DRA_REEVALUATE_OPEN_FAILURES TRUE SYS FGR$AUTOPURGE_JOB FALSE SYS GATHER_STATS_JOB FALSE SYS HM_CREATE_OFFLINE_DICTIONARY FALSE SYS ORA$AUTOTASK_CLEAN TRUE SYS PURGE_LOG TRUE SYS XMLDB_NFS_CLEANUP_JOB FALSE
  • 9/26/2013 31 stats STILL being collected 61 automatic "tasks" 62 "super stealth mode"
  • 9/26/2013 32 63 SQL> select client_name, status 2 from DBA_AUTOTASK_CLIENT; CLIENT_NAME STATUS ------------------------------------ -------- auto optimizer stats collection ENABLED auto space advisor ENABLED sql tuning advisor ENABLED most sites 64 stats every night default options
  • 9/26/2013 33 in this session... 65 default behaviour 66
  • 9/26/2013 34 BAD IDEA 67 collecting statistics 68
  • 9/26/2013 35 BAD IDEA 69 but.... 70
  • 9/26/2013 36 some default behaviour 71 GOOD IDEA 72
  • 9/26/2013 37 collecting some statistics 73 GOOD IDEA 74
  • 9/26/2013 38 75 76 Inflammatory statements which will alienate the audience Presentation Duration 1
  • 9/26/2013 39 we're all hypocrites 77 statistical hypocrisy 78
  • 9/26/2013 40 79 "I need to change some reference data in my system" wrong case
  • 9/26/2013 41 SQL> update … 81 82 "nope...."
  • 9/26/2013 42 83 ITIL 84 Information Technology Infrastructure Library
  • 9/26/2013 43 service call help desk problem recordDONE ! 86 easy....
  • 9/26/2013 44 87 6 months later corrected
  • 9/26/2013 45 89 same site.... 90 "Every night, I would like to potentially change the performance characteristics of every single SQL statement in the database"
  • 9/26/2013 46 91 "no problem...." 92 collecting stats = risk
  • 9/26/2013 47 93 Ensor's paradox 94 1987 performance group bstat/estat tkprof BMC patrol
  • 9/26/2013 48 95 "It is only safe to gather statistics ..." "...when to do so will make no difference" 96 recommendation #1
  • 9/26/2013 49 97 98 unless things are bad... do not change statistics
  • 9/26/2013 50 99 "surely it can't hurt?" 100 10g
  • 9/26/2013 51 101 options=>'GATHER STALE' 102 options=>'GATHER EMPTY'
  • 9/26/2013 52 103 options=>'GATHER AUTO' 104 "no plans changed"
  • 9/26/2013 53 105 "no queries ran slower" still 106 created problems
  • 9/26/2013 54 problem # 1 107 108 dbms_stats
  • 9/26/2013 55 109 the goal of statistics minimise expensive SQL.... 110
  • 9/26/2013 56 added more expensive SQL ! 111 112 SQL> alter session set sql_true = true; Session altered. SQL> begin 2 dbms_stats.gather_table_stats( 3 'DEMO', 4 'PEOPLE); 5 end; 6 /
  • 9/26/2013 57 113 SQL> select 2 count(distinct GENDER), 3 min(GENDER), 4 max(GENDER), 5 count(distinct NAME), 6 min(NAME), 7 max(NAME) ... ... 21 from PEOPLE; hard ! 114
  • 9/26/2013 58 problem #2 116
  • 9/26/2013 59 117 lingering pain invalidation 118
  • 9/26/2013 60 library cache 120
  • 9/26/2013 61 121 select * from PEOPLE insert into PEOPLE select * from ... select ... from PEOPLE, DEPT where ... delete from T where X in ( select PID from PEOPLE ) declare v people.name%type; begin ... SQL> begin 2 dbms_stats.gather_table_stats( 3 'DEMO', 4 'PEOPLE); 5 end; 6 / 122
  • 9/26/2013 62 oracle 9 123 124 SQL> desc DBMS_STATS PROCEDURE GATHER_TABLE_STATS Argument Name Type In/Out Default? ----------------------- -------------- ------ -------- OWNNAME VARCHAR2 IN TABNAME VARCHAR2 IN PARTNAME VARCHAR2 IN DEFAULT ESTIMATE_PERCENT NUMBER IN DEFAULT BLOCK_SAMPLE BOOLEAN IN DEFAULT METHOD_OPT VARCHAR2 IN DEFAULT DEGREE NUMBER IN DEFAULT GRANULARITY VARCHAR2 IN DEFAULT CASCADE BOOLEAN IN DEFAULT STATTAB VARCHAR2 IN DEFAULT STATID VARCHAR2 IN DEFAULT STATOWN VARCHAR2 IN DEFAULT NO_INVALIDATE BOOLEAN IN DEFAULT STATTYPE VARCHAR2 IN DEFAULT FORCE BOOLEAN IN DEFAULT NO_INVALIDATE BOOLEAN IN DEFAULT
  • 9/26/2013 63 125 no_invalidate => false | true ? ? 126 oracle 10
  • 9/26/2013 64 127 no_invalidate => auto 128 SQL> select 2 x.ksppinm name, 3 y.kspftctxvl value 4 from 5 sys.x$ksppi x, 6 sys.x$ksppcv2 y 7 where 8 x.indx+1 = y.kspftctxpn and 9 x.ksppinm = '_optimizer_invalidation_period' NAME VALUE ------------------------------ ------------ _optimizer_invalidation_period 18000
  • 9/26/2013 65 129 SQL> exec dbms_stats.gather_table_stats( user,'PEOPLE'); select * from PEOPLE insert into PEOPLE select * from ... select ... from PEOPLE, DEPT where ... delete from T where X in ( select PID from PEOPLE ) declare v people.name%type; begin ... 130 better ?
  • 9/26/2013 66 131 hard parse storm avoided 132 5 hours before problems
  • 9/26/2013 67 recap 133 134 "surely it can't hurt?"
  • 9/26/2013 68 gathering statistics 135 really hard core SQL belted CPU with hard parsing everything ran the same 136
  • 9/26/2013 69 137 unless things are bad... do not change statistics 138 Inflammatory statements which will alienate the audience Presentation Duration 1 2
  • 9/26/2013 70 139 we don't know if things are bad 140 until its too late
  • 9/26/2013 71 141 142 “why are SQL's so slow” users phone “the stats out of date”
  • 9/26/2013 72 143
  • 9/26/2013 73 145 146 2 hours to gather stats
  • 9/26/2013 74 147 148 collect stats
  • 9/26/2013 75 149 in readiness 150
  • 9/26/2013 76 151 you can ! 152 pending statistics
  • 9/26/2013 77 153 "pending" - private "published" – optimizer uses 154 SQL> begin 2 dbms_stats.set_schema_prefs( 3 ownname=>'DEMO', 4 pname=>'PUBLISH', 5 pvalue=>'FALSE'); 6 end; database, table
  • 9/26/2013 78 155 SQL> alter session set 2 optimizer_use_pending_statistics = true; 156 SQL> begin 2 dbms_stats.publish_pending_stats( 3 ownname=>'DEMO', 4 tabname=>null); 5 end;
  • 9/26/2013 79 157 recommendation #2 158 when collecting statistics
  • 9/26/2013 80 159 PUBLISH = false 160 even if you immediately publish
  • 9/26/2013 81 161 atomic publication 162 and don't wait 5 hours
  • 9/26/2013 82 163 _optimizer_invalidation_period 164 then for most tables
  • 9/26/2013 83 165 dbms_stats.lock_table_stats 166 collecting system stats
  • 9/26/2013 84 167 different story 168 absolutely vital… …to have them
  • 9/26/2013 85 169 some history 170 maximize I/O throughput
  • 9/26/2013 86 171 SQL> set autotrace traceonly explain SQL> select * from T; --------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| --------------------------------------------------------------- | 0 | SELECT STATEMENT | | 53459 | 4489K| 113 (1)| | 1 | TABLE ACCESS FULL| T | 53459 | 4489K| 113 (1)| --------------------------------------------------------------- SQL> select value 2 from v$spparameter 3 where name = 'db_file_multiblock_read_count'; VALUE ----------- 16 172 SQL> alter session set events 2 = '10046 trace name context forever, level 8'; SQL> select * from T; PARSING IN CURSOR #22 len=15 dep=0 uid=124 oct=3 lid=124 ... select * from T END OF STMT PARSE #22:c=10000,e=108,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4 EXEC #22:c=0,e=65,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4 WAIT #22: nam='db file scattered read' ela= 3387 file#=5 block#=13122 blocks=16 obj#=199963 WAIT #22: nam='db file scattered read' ela= 3188 file#=5 block#=13234 blocks=16 obj#=199963 WAIT #22: nam='db file scattered read' ela= 3125 file#=5 block#=13250 blocks=16 obj#=199963 WAIT #22: nam='db file scattered read' ela= 3255 file#=5 block#=13266 blocks=16 obj#=199963 WAIT #22: nam='db file scattered read' ela= 3369 file#=5 block#=13282 blocks=16 obj#=199963
  • 9/26/2013 87 173 SSTIOMAX 174 1 MB
  • 9/26/2013 88 175 db_block_size = 8192 176 db_file_multiblock_read_count = 128
  • 9/26/2013 89 177 ? 178
  • 9/26/2013 90 179 single block read (8KB)  multi block read (1MB) 180 the solution ?
  • 9/26/2013 91 181 system stats (9i) 182 SQL> select pname, pval1 2 from sys.aux_stats$ 3 / PNAME PVAL1 ------------------------------ ---------- SREADTIM 4.065 MREADTIM 6.173 CPUSPEED 567 MBRC 12 MAXTHR 48203776 SLAVETHR -1
  • 9/26/2013 92 183 use MBRC to cost 184 use db_file_multiblock_read_count to read
  • 9/26/2013 93 185 Inflammatory statements which will alienate the audience Presentation Duration 1 2 3 186 forget about system statistics
  • 9/26/2013 94 187 do not gather them 188 ?
  • 9/26/2013 95 189 190 absolutely vital… …to have them set change
  • 9/26/2013 96 191 problem #1 192
  • 9/26/2013 97 193 problem #2 194 monitoring for bad....
  • 9/26/2013 98 195 optimizing for bad 196 recommendation #3
  • 9/26/2013 99 197 gather into STAT table 198 monitor
  • 9/26/2013 100 199 set once 200 the defaults are probably fine
  • 9/26/2013 101 201 SQL> select pname, pval1 2 from sys.aux_stats$ 3 / PNAME PVAL1 ---------------------- ---------- IOSEEKTIM 10 (ms) IOTFRSPEED 4096 (bytes/ms) SREADTIM = IOSEEKTIM + 8k / IOTFRSPEED = 10ms MREADTIM = IOSEEKTIM + mbrc * 8k / IOTFRSPEED = 26ms 202 10.2 and above
  • 9/26/2013 102 203 db_file_multiblock_read_count _db_file_exec_read_count _db_file_optimizer_read_count 204 dbms_resource_manager to calibrate
  • 9/26/2013 103 205 so far... 206 not changing statistics
  • 9/26/2013 104 207 eventually.... 208 the time will come...
  • 9/26/2013 105 209 one possible reason 210 even if stats unchanged …
  • 9/26/2013 106 211 … your plans might ! 212 cardinality is everything 212
  • 9/26/2013 107 213 214
  • 9/26/2013 108 215 same with Oracle 215 SQL> create table T as 2 select to_date('01-AUG-2013')+ 3 trunc(dbms_random.value(0,7)) dte, 4 rpad('padding',20) padding 5 from dual 6 connect by level <= 100000; Table created.
  • 9/26/2013 109 SQL> select dte, count(*) 2 from t 3 group by dte 4 order by 1; DTE COUNT(*) --------- ---------- 01-AUG-13 14334 02-AUG-13 14222 03-AUG-13 14167 04-AUG-13 14510 05-AUG-13 14346 06-AUG-13 14349 07-AUG-13 14072 218 15,000 rows per day always
  • 9/26/2013 110 SQL> exec dbms_stats.gather_table_stats(user,'T') PL/SQL procedure successfully completed. SQL> create index IX on T ( dte ) ; Index created. pre 12c SQL> select * from t where dte = '03-AUG-2013'; -------------------------------------------------- | Id | Operation | Name | Rows | Bytes | -------------------------------------------------- | 0 | SELECT STATEMENT | | 14286 | 404K| |* 1 | TABLE ACCESS FULL| T | 14286 | 404K| --------------------------------------------------
  • 9/26/2013 111 221 a week later… 222 another 15,000 rows per day
  • 9/26/2013 112 SQL> insert into T 2 select to_date('08-AUG-2013')+ 3 trunc(dbms_random.value(0,7)) dte, 4 rpad('padding',20) padding 5 from dual 6 connect by level <= 100000; 100000 rows created. SQL> select dte, count(*) 2 from t 3 group by dte 4 order by 1; DTE COUNT(*) --------- ---------- 01-AUG-13 14334 ... 07-AUG-13 14072 08-AUG-13 14261 09-AUG-13 14106 10-AUG-13 14410 11-AUG-13 14289 12-AUG-13 14358 13-AUG-13 14252 14-AUG-13 14324
  • 9/26/2013 113 225 statistics unchanged 226
  • 9/26/2013 114 227 SQL> select * from t where dte = '12-AUG-2013'; ---------------------------------------------------- | Id | Operation | Name | Rows | ---------------------------------------------------- | 0 | SELECT STATEMENT | | 2381 | | 1 | TABLE ACCESS BY INDEX ROWID| T | 2381 | |* 2 | INDEX RANGE SCAN | IX | 2381 | ----------------------------------------------------
  • 9/26/2013 115 SQL> select * from t where dte = '14-AUG-2013'; ---------------------------------------------------- | Id | Operation | Name | Rows | ---------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | |* 2 | INDEX RANGE SCAN | IX | 1 | ---------------------------------------------------- 230 why ?
  • 9/26/2013 116 SQL> select low_value, high_value 2 from user_tab_columns 3 where table_name = 'T' 4 and column_name = 'DTE'; LOW_VALUE HIGH_VALUE -------------------- ----------------- 78710801010101 78710807010101 SQL> set serverout on SQL> declare 2 res date; 3 begin 4 dbms_stats.convert_raw_value( 5 '78710801010101',res); 6 dbms_output.put_line(res); 7 dbms_stats.convert_raw_value( 8 '78710807010101',res); 9 dbms_output.put_line(res); 10 end; 11 / 01-AUG-13 07-AUG-13
  • 9/26/2013 117 01-AUG-13 07-AUG-13 234 when the time comes…
  • 9/26/2013 118 235 as accurate as possible 236 as cheaply as possible
  • 9/26/2013 119 237 statistics accurately 238 extended statistics
  • 9/26/2013 120 239 some real data 239 240 SQL> desc VEHICLE Name Null? Type -------------------------- -------- ------------- ID NUMBER MAKE VARCHAR2(12) MODEL VARCHAR2(12) ... SQL> select count(*) 2 from VEHICLE; COUNT(*) ------------ 4,770,662 240
  • 9/26/2013 121 241 default stats not enough 241 242 SQL> select count(*) 2 from VEHICLE 3 where MAKE = 'TOYOTA'; COUNT(*) ---------- 608822 ------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost | ------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 7 | 18| | 1 | SORT AGGREGATE | | 1 | 7 | | |* 2 | INDEX RANGE SCAN| MAKE_IX | 6325 | 44275 | 18| ------------------------------------------------------------ 242
  • 9/26/2013 122 243 histogram 243 244 SQL> begin 2 dbms_stats.gather_table_stats(user,'VEHICLE', 3 method_opt=>'for all columns size 1,'|| 4 'for columns MAKE size auto,'|| 5 'for columns MODEL size auto'); 6 end; 7 / PL/SQL procedure successfully completed. 244
  • 9/26/2013 123 245 SQL> select count(*) 2 from VEHICLE 3 where MAKE = 'TOYOTA'; COUNT(*) ---------- 608822 ----------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost ----------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 7 | 1465 | 1 | SORT AGGREGATE | | 1 | 7 | |* 2 | INDEX RANGE SCAN| MAKE_IX | 589K| 4027K| 1465 ----------------------------------------------------------- 245 246 make AND model 246
  • 9/26/2013 124 247 SQL> select count(*) 2 from VEHICLE 3 where MAKE = 'TOYOTA' 4 and MODEL = 'COROLLA'; COUNT(*) ---------- 257364 ----------------------------------------------------------------- | Id | Operation | Name | Rows | ----------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS BY INDEX ROWID BATCHED| VEHICLE | 301 | |* 3 | INDEX RANGE SCAN | MODEL_IX | 2436 | ----------------------------------------------------------------- 247 248 40% (!) of Toyotas are Corollas 248
  • 9/26/2013 125 249 two things 249 250
  • 9/26/2013 126 251 251 252 2) no correlation 10g and before 252
  • 9/26/2013 127 253 11g+ 253 254 SQL> select 2 DBMS_STATS.CREATE_EXTENDED_STATS( 3 user, 'VEHICLE','(MAKE,MODEL)') tag 4 from dual; TAG ---------------------------------- SYS_STU8QPK2S$PEWHARK2CP3#1F#G 254
  • 9/26/2013 128 255 SQL> begin 2 dbms_stats.gather_table_stats(user,'VEHICLE', 3 method_opt=> 4 'for columns (make,model) size 254'); 5 end; 6 / PL/SQL procedure successfully completed. 255 256 SQL> select count(*) 2 from VEHICLE 3 where MAKE = 'TOYOTA' 4 and MODEL = 'COROLLA'; COUNT(*) ---------- 257364 ---------------------------------------------------------------- | Id | Operation | Name | Rows | ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS BY INDEX ROWID BATCHED| VEHICLE | 248K| |* 3 | INDEX RANGE SCAN | MODEL_IX | 2436| ---------------------------------------------------------------- 256
  • 9/26/2013 129 257 which columns to collect ? 257 258 11.2+ column groups 258
  • 9/26/2013 130 259 SQL> exec DBMS_STATS.SEED_COL_USAGE(NULL,NULL,1800); 259 260 note ... 12c 260
  • 9/26/2013 131 261 can be created automatically 261 262 its just another column 262
  • 9/26/2013 132 263 SQL> select "SYS_STU8QPK2S$PEWHARK2CP3#1F#G" 2 from vehicle 3 where rownum < 10; SYS_STU8QPK2S$PEWHARK2CP3#1F#G ------------------------------ 1.2706E+19 1.8075E+19 7.9949E+18 1.1730E+19 6.7142E+18 1.1730E+19 1.0779E+19 5.4051E+18 7.3555E+18 263 264 SQL> SELECT extension_name, extension 2 FROM USER_STAT_EXTENSIONS 3 WHERE table_name = 'VEHICLE'; EXTENSION_NAME EXTENSION ------------------------------ ----------------- SYS_STU8QPK2S$PEWHARK2CP3#1F#G ("MAKE","MODEL") 264
  • 9/26/2013 133 265 SQL> select SYS_OP_COMBINED_HASH(make,model) hashval, 2 "SYS_STU8QPK2S$PEWHARK2CP3#1F#G" colval 3 from VEHICLE 4 where rownum < 10; HASHVAL COLVAL ---------- ---------- 1.2706E+19 1.2706E+19 1.8075E+19 1.8075E+19 7.9949E+18 7.9949E+18 1.1730E+19 1.1730E+19 6.7142E+18 6.7142E+18 1.1730E+19 1.1730E+19 1.0779E+19 1.0779E+19 5.4051E+18 5.4051E+18 7.3555E+18 7.3555E+18 265 266 PARSING IN CURSOR #28 alter table "SH"."VEHICLE" add (SYS_STU8QPK2S$PEWHARK2CP3#1F#G as (SYS_OP_COMBINED_HASH(MAKE,MODEL)) virtual BY USER for statistics); END OF STMT 266
  • 9/26/2013 134 267 virtual column 267 be careful 268 268 SYS_OP_COMBINED_HASH
  • 9/26/2013 135 269 hash means equality 269 270 SQL> select count(*) 2 from VEHICLE 3 where MAKE in ('FORD','TOYOTA') 4 and MODEL = 'COROLLA'; COUNT(*) ---------- 214468 ----------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost ----------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 14 | 1921 | 1 | SORT AGGREGATE | | 1 | 14 | |* 2 | VIEW | index$_join$_001 | 77818 | 1063K| 1921 |* 3 | HASH JOIN | | | | |* 4 | INDEX RANGE SCAN | MODEL_IX | 77818 | 1063K| 502 | 5 | INLIST ITERATOR | | | | |* 6 | INDEX RANGE SCAN| MAKE_IX | 77818 | 1063K| 2086 ----------------------------------------------------------------------- 270
  • 9/26/2013 136 271 better in 12 271 272 SQL> select count(*) 2 from VEHICLE 3 where MAKE in ('FORD','TOYOTA') 4 and MODEL = 'COROLLA'; COUNT(*) ---------- 214468 ----------------------------------------------------------------- | Id | Operation | Name | Rows | ----------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS BY INDEX ROWID BATCHED| VEHICLE | 272K| |* 3 | INDEX RANGE SCAN | MODEL_IX | 2436 | ----------------------------------------------------------------- 272
  • 9/26/2013 137 273 273 11.2+ 274 actual versus estimate 274
  • 9/26/2013 138 275 SQL> select /*+ GATHER_PLAN_STATISTICS */ count(*) 2 from VEHICLE 3 where MAKE = 'FORD' 4 and MODEL = 'FOCUS'; COUNT(*) ---------- 214468 SQL> SELECT * 2 FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR( 3 NULL, NULL, 'ALLSTATS LAST')); ---------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | ---------------------------------------------------------------- | 1 | SORT AGGREGATE | | 1 | 1 | 1 | |* 2 | TABLE ACCESS FULL| VEHICLE | 1 | 220K| 214K| ----------------------------------------------------------------- 275 276 276 employ someone....
  • 9/26/2013 139 277 277 check every query... 278 SQL> select /*+ GATHER_PLAN_STATISTICS */ count(*) 2 from VEHICLE 3 where MAKE = 'FORD' 4 and MODEL = 'FOCUS'; COUNT(*) ---------- 214468 SQL> SELECT * 2 FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR( 3 NULL, NULL, 'ALLSTATS LAST')); ----------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | ----------------------------------------------------------------- | 1 | SORT AGGREGATE | | 1 | 1 | 1 | |* 2 | TABLE ACCESS FULL| VEHICLE | 1 | 220K| 214K| ----------------------------------------------------------------- 278 "ok"
  • 9/26/2013 140 279 279 280 280 but just maybe ....
  • 9/26/2013 141 281 281 ... someone already is 282 cool
  • 9/26/2013 142 SQL> create table EMP as 2 select rownum empno, 3 mod(rownum,10) jobid, 4 mod(rownum,10)*1000 salary, 5 mod(rownum,50)+1 deptno 6 from dual 7 connect by rownum < 100000; SQL> create table DEPT as 2 select rownum deptno, 3 'dept'||rownum dname 4 from dual 5 connect by rownum <= 100; 283 100,000 rows 100 rows SQL> exec dbms_stats.gather_table_stats(user,'EMP'); SQL> exec dbms_stats.gather_table_stats(user,'DEPT'); SQL> create index EMP_IX on EMP ( deptno ); SQL> create index DEPT_IX on DEPT ( deptno ); 284
  • 9/26/2013 143 SQL> select e.empno, d.dname 2 from emp e, dept d 3 where d.deptno = e.deptno 4 and e.jobid < 3 5 and e.salary > 9000; no rows selected ---------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | 1 | MERGE JOIN | | 1500 | 36000 | | 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 100 | 1000 | | 3 | INDEX FULL SCAN | DEPT_IX | 100 | | |* 4 | SORT JOIN | | 1500 | 21000 | |* 5 | TABLE ACCESS FULL | EMP | 1500 | 21000 | ---------------------------------------------------------------- 285 4 and e.jobid < 3 5 and e.salary > 9000; hard to optimize re-run the query 286
  • 9/26/2013 144 287 no anythingchanges to SQL> select e.empno, d.dname 2 from emp e, dept d 3 where d.deptno = e.deptno 4 and e.jobid < 3 5 and e.salary > 9000; no rows selected --------------------------------------------------- | Id | Operation | Name | Rows | Bytes | --------------------------------------------------- | 0 | SELECT STATEMENT | | | | |* 1 | HASH JOIN | | 67 | 1608 | | 2 | TABLE ACCESS FULL| DEPT | 1 | 10 | |* 3 | TABLE ACCESS FULL| EMP | 3333 | 46662 | --------------------------------------------------- Note ----- - statistics feedback used for this statement 288
  • 9/26/2013 145 11.2 289 the optimizer knows what "hard" is 290
  • 9/26/2013 146 cardinality feedback 291 statistics feedback 292 12c
  • 9/26/2013 147 its not continuous 293 12c better 294
  • 9/26/2013 148 see it ! 295 SQL> select sql_id, 2 child_number, 3 is_reoptimizable, 4 is_shareable 2 from v$sql s 3 where sql_text like 'select ...'; SQL_ID CHILD_NUMBER I I ------------- ------------ - - 68vghuy0cr298 0 Y N 68vghuy0cr298 1 N Y 296
  • 9/26/2013 149 hangs around 297 SQL> exec dbms_spd.flush_sql_plan_directive; PL/SQL procedure successfully completed. SQL> select d.type, d.reason, o.object_name 2 from dba_sql_plan_directives d, 3 dba_sql_plan_dir_objects o 4 where o.directive_id = d.directive_id 5 and o.owner = 'SCOTT'; TYPE REASON OBJ ---------------- ------------------------------------ ----- DYNAMIC_SAMPLING SINGLE TABLE CARDINALITY MISESTIMATE DEPT 298
  • 9/26/2013 150 problem with statistics feedback 299 pay the price once ! 300
  • 9/26/2013 151 301 302 "I'll take a different route tomorrow"
  • 9/26/2013 152 303 the reality... 304
  • 9/26/2013 153 305 adaptive optimization 12c 306 accurate stats not always possible...
  • 9/26/2013 154 307 you can't have stats 308 on everything !
  • 9/26/2013 155 309 when stats wont do... 310 ... use the real data
  • 9/26/2013 156 311 dynamic sampling 312 SQL> drop table TOUGH_DATA purge; Table dropped. SQL> create table TOUGH_DATA nologging as 2 select 3 rownum pk, 4 dbms_random.string('U',10) str 5 from dual 6 connect by level < 1000000 7 / Table created. SQL> exec dbms_stats.gather_table_stats( user,'TOUGH_DATA')
  • 9/26/2013 157 313 SQL> select count(*) 2 from TOUGH_DATA 3 where str like '%XX' 4 / COUNT(*) ---------- 1452 hard 314 SQL> select count(*) 2 from TOUGH_DATA 3 where str like '%XX' 4 / ------------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| TOUGH_DATA | 50000 | ------------------------------------------------- 5% assumption
  • 9/26/2013 158 315 SQL> select /*+ dynamic_sampling(t 2) */ count(*) 2 from TOUGH_DATA t 3 where str like '%XX' 4 / ------------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| TOUGH_DATA | 1252 | ------------------------------------------------- 316 simple queries as well
  • 9/26/2013 159 317 SQL> create table EASY_DATA nologging as 2 select 3 rownum pk, 4 chr(65+trunc(rownum/40000)) str 5 from dual 6 connect by level < 1000000 7 / Table created. SQL> exec dbms_stats.gather_table_stats(user,'EASY_DATA') PL/SQL procedure successfully completed. A,A,A,A,A,….B,B,B,B,.....1,2,3,4,5,6...... 318 SQL> select count(*) 2 from EASY_DATA 3 where str = 'F' 4 and pk > 900000; COUNT(*) ---------- 0
  • 9/26/2013 160 319 SQL> select count(*) 2 from EASY_DATA 3 where str = 'F' 4 and pk > 900000; ------------------------------------------------ | Id | Operation | Name | Rows | ------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| EASY_DATA | 4000 | ------------------------------------------------ 320 SQL> select /*+ dynamic_sampling(t 2) */ count(*) 2 from EASY_DATA t 3 where str = 'F' 4 and pk > 900000; ------------------------------------------------ | Id | Operation | Name | Rows | ------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| EASY_DATA | 23 | ------------------------------------------------
  • 9/26/2013 161 321 or ... 322 someone else's problem
  • 9/26/2013 162 323 12c 324 dynamic sampling statistics
  • 9/26/2013 163 325 SQL> select /*+ dynamic_sampling(t 2) */ count(*) 2 from TOUGH_DATA t 3 where str like '%XX' 4 / ------------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| TOUGH_DATA | 1252 | ------------------------------------------------- 326 SQL> alter session set optimizer_dynamic_sampling = 11; SQL> select count(*) 2 from TOUGH_DATA t 3 where str like '%XX' 4 / ------------------------------------------------- | Id | Operation | Name | Rows | ------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | |* 2 | TABLE ACCESS FULL| TOUGH_DATA | 1252 | ------------------------------------------------- Note ----- - dynamic statistics used (level=AUTO) no hint
  • 9/26/2013 164 327 11.2+ 328 parallel queries (may be) sampled
  • 9/26/2013 165 329 SQL> select count(*) 2 from EASY_DATA t 3 where str = 'F' 4 and pk > 900000; ---------------------------------------------------- | Id | Operation | Name | Rows | ---------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | | 1 | | 2 | PX COORDINATOR | | | | 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | | 4 | SORT AGGREGATE | | 1 | | 5 | PX BLOCK ITERATOR | | 4 | |* 6 | TABLE ACCESS FULL| EASY_DATA | 4 | ---------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=4) 330 low frequency
  • 9/26/2013 166 331 high cost 332 statistics cheaply
  • 9/26/2013 167 333 SQL> desc DBA_TABLES Name Null? Type ----------------------------- -------- ------------- OWNER NOT NULL VARCHAR2(30) TABLE_NAME NOT NULL VARCHAR2(30) COLUMN_NAME NOT NULL VARCHAR2(30) ... NUM_ROWS NUMBER 334 SQL> desc DBA_TAB_COLS Name Null? Type ----------------------------- -------- ------------- OWNER NOT NULL VARCHAR2(30) TABLE_NAME NOT NULL VARCHAR2(30) COLUMN_NAME NOT NULL VARCHAR2(30) ... NUM_DISTINCT NUMBER LOW_VALUE RAW(32) HIGH_VALUE RAW(32) ...
  • 9/26/2013 168 335 SQL> desc PEOPLE Name Null? Type ----------------------------- -------- ------------- PID NUMBER GENDER CHAR(1) NAME VARCHAR2(47) AGE NUMBER 336 SQL> alter session set sql_true = true; Session altered. SQL> begin 2 dbms_stats.gather_table_stats( 3 'DEMO', 4 'PEOPLE); 5 end; 6 /
  • 9/26/2013 169 337 select /*+ no_parallel(t) no_parallel_index(t) dbms_stats cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring no_substrb_pad */ count(*), ... count("GENDER"), count(distinct "GENDER"), substrb(dump(min("GENDER"),16,0,32),1,120), substrb(dump(max("GENDER"),16,0,32),1,120), ... from "MCDONAC"."PEOPLE" t count("GENDER") count(distinct "GENDER") min("GENDER") max("GENDER") 338 count("GENDER") count(distinct "GENDER") min("GENDER") max("GENDER") one pass one pass one pass hard
  • 9/26/2013 170 339 SQL> select count(*) from PEOPLE; COUNT(*) ------------ 500,000,000 Elapsed: 00:04:43.73 340 SQL> select count(distinct GENDER) g 2 from PEOPLE; G ------------ 2 Elapsed: 00:13:12.92
  • 9/26/2013 171 341 SQL> begin 2 dbms_stats.gather_table_stats( 3 user, 4 'PEOPLE', 5 estimate_percent=>25); 6 end; 7 / ERROR at line 1: ORA-01652: unable to extend temp segment by 128 in tablespace TEMP Elapsed: 00:16:37.12 342 11g
  • 9/26/2013 172 343 one pass ~NDV 344 HASH 12 6 3 12 3 7 11 12 33 6 11 12 6 45 15 7 15 17 45 6 17 12 6 3 7 11 33 45 15 17
  • 9/26/2013 173 345 NDV = 9 12 6 3 7 11 33 45 15 17 346 what about large NDV ? 12 6 3 7 11 33 45 15 17 21 92 71 34 56 615 2 41 64 91 73
  • 9/26/2013 174 347 "Big" HASH 348 NDV = remaining hashes x 2^number of splits
  • 9/26/2013 175 349 demo 350 SQL> create table ONE_PASS nologging 2 as select substr(text,1,1) single_char 3 from DBA_SOURCE; Table created. SQL> select count(distinct single_char) ndv 2 from one_pass; NDV ---------- 83
  • 9/26/2013 176 351 0 127 16 buckets... 352 64 127
  • 9/26/2013 177 353 96 127 354 SQL> set serverout on SQL> declare 2 type t_bucket is table of varchar2(1); 3 l_synopsis t_bucket; 4 l_splits number := 0; 5 l_hash int; 6 l_min_val int := 0; 7 l_synopsis_size int := 16; 8 begin 9 for i in ( select single_char from one_pass ) loop 10 l_hash := ascii(i.single_char); 11 12 if l_synopsis.count = l_synopsis_size then 13 l_min_val := 14 case 15 when l_min_val = 0 then 64 16 when l_min_val = 64 then 96 17 when l_min_val = 96 then 112 18 when l_min_val = 112 then 120 19 end; 20 l_splits := l_splits + 1;
  • 9/26/2013 178 355 22 23 for j in 1 .. l_min_val loop 24 if l_synopsis.exists(j) then 25 l_synopsis.delete(j); 26 end if; 27 end loop; 28 end if; 29 30 if l_hash > l_min_val then 31 l_synopsis(l_hash) := 'Y'; 32 end if; 33 end loop; 34 dbms_output.put_line(l_synopsis.count * 35 power(2,l_splits)); 36 end; 37 / Splitting, keeping entries above 64 Splitting, keeping entries above 96 Splitting, keeping entries above 112 88 356 the reality 16384 bucket limit 18,446,744,073,709,551,616 hash range
  • 9/26/2013 179 357 SQL> begin 2 dbms_stats.gather_table_stats( 3 user, 4 'PEOPLE', 5 estimate_percent=>25); 6 end; 7 / PL/SQL procedure successfully completed. Elapsed: 00:05:39.82 358 recommendation #4
  • 9/26/2013 180 359 estimate_percent use DEFAULT size (AUTO) 360 only for columns without histograms* (* better in 12c)
  • 9/26/2013 181 361 cheaply .... other options 362 do it elsewhere...
  • 9/26/2013 182 363 SQL> alter database convert to snapshot standby; SQL> begin 2 dbms_scheduler.open_window( 3 'MONDAY_WINDOW',force=>true) 4 end; 5 / [wait] SQL> exec dbms_stats.export_database_stats(...) SQL> insert into DB@target select * from STATTAB; SQL> alter database convert to physical standby; 364 histograms
  • 9/26/2013 183 365 Inflammatory statements which will alienate the audience Presentation Duration 1 2 3 4 366 "histograms are only goodness. Only use METHOD_OPT auto" Maria Colgan, Oct 2013 (2 days ago)
  • 9/26/2013 184 367 similar quote 368 histograms...
  • 9/26/2013 185 369 ... SUCK 370 not really but...
  • 9/26/2013 186 371 two problems 372 1) risks when gathering
  • 9/26/2013 187 373 missing values in sample 374
  • 9/26/2013 188 375 SQL> select availability, count(*) 2 from bet_events 3 group by availability; AVAILABILITY COUNT(*) -------------------- ---------- Pending 3404 Open 18 Closed 12345 Completed 3840938 ... ... 376 SQL> begin 2 dbms_stats.gather_table_stats('','bet_events', 3 method_opt=>'for all columns size 1, 4 for columns availability size 10'); 5 end; 6 / SQL> set autotrace traceonly explain SQL> select * from bet_events 2 where availability = 'UnProcessed'; ------------------------------------------------ | Id | Operation | Name | Rows | ------------------------------------------------ | 0 | SELECT STATEMENT | | 18 | |* 1 | TABLE ACCESS FULL| BET_EVENTS | 18 | ------------------------------------------------
  • 9/26/2013 189 377 SQL> select availability, count(*) 2 from bet_events 3 group by availability; AVAILABILITY COUNT(*) -------------------- ---------- Pending 3404 Open 18 Closed 12345 Completed 3840938 ... ... 56 378 SQL> set autotrace traceonly explain SQL> select * from bet_events 2 where availability = 'UnProcessed'; ---------------------------------------------- | Id | Operation | Name | Rows| ---------------------------------------------- | 0 | SELECT STATEMENT | | 929 | |* 1 | TABLE ACCESS FULL| BET_EVENTS | 929 | ----------------------------------------------
  • 9/26/2013 190 379 missing values in height balanced 380 12c a lot better... top n frequency hybrid "histograms are only goodness"maybe
  • 9/26/2013 191 381 2) the worst thing in 9i
  • 9/26/2013 192 383 SQL> variable b1 number SQL> exec :b1 := 1 SQL> select /*+ gather_plan_statistics */ * 2 from MY_100K_ROWS 3 where r < :b1; ------------------------------------------------------------ | Id | Operation | Name | E-Rows | A-Rows | ------------------------------------------------------------ | 0 | SELECT STATEMENT | | | 0 | |* 1 | TABLE ACCESS FULL| MY_100K_ROWS | 5000 | 0 | ------------------------------------------------------------ 5% 384
  • 9/26/2013 193 385 histograms 386 10g
  • 9/26/2013 194 387 method_opt for all columns size auto 388 sys.col_usage$
  • 9/26/2013 195 389 SQL> desc SYS.COL_USAGE$ Name Null? Type ----------------------- -------- --------- OBJ# NUMBER INTCOL# NUMBER EQUALITY_PREDS NUMBER EQUIJOIN_PREDS NUMBER NONEQUIJOIN_PREDS NUMBER RANGE_PREDS NUMBER LIKE_PREDS NUMBER NULL_PREDS NUMBER TIMESTAMP DATE 390 Oracle doesn’t know ...
  • 9/26/2013 196 391 ... how your app works 392 ... what your app is !
  • 9/26/2013 197 393 example 1 394 SQL> create table T 2 as select * from dba_objects; Table created. SQL> exec dbms_stats.gather_table_stats('','T'); PL/SQL procedure successfully completed.
  • 9/26/2013 198 395 SQL> select COLUMN_NAME,NUM_DISTINCT, 2 ( select count(*) 3 from all_tab_histograms 4 where owner = a.owner 5 and table_name = a.table_name 6 and column_name = a.column_name ) 7 from all_tab_cols a 8 where table_name = 'T' 9 order by column_id; COLUMN_NAME NUM_DISTINCT HIST_CNT ------------------------------ ------------ ---------- OWNER 25 2 OBJECT_NAME 53000 2 SUBOBJECT_NAME 230 2 OBJECT_ID 90808 2 396 then...
  • 9/26/2013 199 397 398
  • 9/26/2013 200 399 SQL> select intcol#,equality_preds, 2 range_preds,timestamp 3 from sys.col_usage$ 4 where obj# = (select object_id 5 from obj 6 where object_name = 'T' ); INTCOL# EQUALITY_PREDS RANGE_PREDS TIMESTAMP ---------- -------------- ----------- --------- 1 1 0 24-SEP-13 400 SQL> exec dbms_stats.gather_table_stats('','T'); SQL> select COLUMN_NAME,NUM_DISTINCT, 2 ( select count(*) 3 from all_tab_histograms 4 where owner = a.owner 5 and table_name = a.table_name 6 and column_name = a.column_name ) 7 from all_tab_cols a 8 where table_name = 'T' 9 order by column_id; COLUMN_NAME NUM_DISTINCT HIST_CNT ------------------------------ ------------ ---------- OWNER 25 25 OBJECT_NAME 53000 2 SUBOBJECT_NAME 230 2 OBJECT_ID 90808 2
  • 9/26/2013 201 401 Oracle doesn’t know your app ! 402 example 2
  • 9/26/2013 202 403 boudary conditions 404 SQL> create table T ( 2 skew varchar2(10), 3 even number); Table created. SQL> insert into T 2 select 3 case 4 when rownum > 99995 then 'SPECIAL' 5 else dbms_random.string('U',8) 6 end, 7 mod(rownum,200) 8 from dual 9 connect by level <= 100000 10 / 100000 rows created. 5 special values even distribution
  • 9/26/2013 203 405 SQL> exec dbms_stats.gather_table_stats( user,'T', estimate_percent=>null); PL/SQL procedure successfully completed. SQL> select COLUMN_NAME,NUM_DISTINCT,DENSITY, 2 ( select count(*) 3 from user_tab_histograms 4 where table_name = 'T' 5 and column_name = c.column_name ) 6 from user_tab_cols c 7 where table_name = 'T' 8 order by table_name,COLUMN_ID 9 / COLUMN_NAME NUM_DISTINCT DENSITY HIST_CNT ------------------- ------------ ---------- ---------- SKEW 99996 .00001 2 EVEN 200 .005 2 406 SQL> select * from T where skew = 'SPECIAL'; SKEW VAL ---------- ---------- SPECIAL 196 SPECIAL 197 SPECIAL 198 SPECIAL 199 SPECIAL 0 5 rows selected. SQL> select * from T where even = 5; TAG VAL ---------- ---------- IBRXGVIE 5 [snip] 500 rows selected.
  • 9/26/2013 204 407 SQL> exec dbms_stats.gather_table_stats( user,'T', estimate_percent=>null); PL/SQL procedure successfully completed. SQL> select COLUMN_NAME,NUM_DISTINCT,DENSITY, 2 ( select count(*) 3 from user_tab_histograms 4 where table_name = 'T' 5 and column_name = c.column_name ) 6 from user_tab_cols c 7 where table_name = 'T' 8 order by table_name,COLUMN_ID 9 / COLUMN_NAME NUM_DISTINCT DENSITY HIST_CNT ------------------- ------------ ---------- ---------- SKEW 99996 .00001 2 EVEN 200 .000005 200 408 recommendation #5
  • 9/26/2013 205 409 histograms... ...can be very cool suck 410 explicit control for each table
  • 9/26/2013 206 411 set_schema_prefs for all columns size 1 get NDV benefit...better in 12c 412 set_table_prefs for column CCC size nnn
  • 9/26/2013 207 413 one more useful tool 414
  • 9/26/2013 208 415 416 SQL> desc CONFERENCE_ATTENDEES Name Null? Type ----------------------------- -------- -------------- PID NUMBER GENDER CHAR(1) NAME VARCHAR2(40) AGE NUMBER
  • 9/26/2013 209 417 SQL> select 2 count(distinct GENDER) NDV 3 from 4 CONFERENCE_ATTENDEES NDV ---------------------- ??? 418 SQL> select 2 ntile(GENDER) over ( ... ) 3 from 4 CONFERENCE_ATTENDEES
  • 9/26/2013 210 419 set_table_stats num_rows avg_row_len 420 set_column_stats high_value num_distinct
  • 9/26/2013 211 421 when all else fails ... 422 ... lie and cheat
  • 9/26/2013 212 423 424 no
  • 9/26/2013 213 425 analyze ... validate structure 426 SQL> desc INDEX_STATS Name Null? Type ----------------------------- -------- -------------- HEIGHT NUMBER BLOCKS NUMBER NAME VARCHAR2(30) PARTITION_NAME VARCHAR2(30) ... OPT_CMPR_COUNT NUMBER OPT_CMPR_PCTSAVE NUMBER
  • 9/26/2013 214 427 wrap up 428 don't collect stats .... unless don't collect system stats ... unless don't collect histograms ... unless default estimate size (NDV) lie and cheat
  • 9/26/2013 215 Connor McDonald 429 www.oracledba.co.uk connormcdonald.wordpress.com @connor_mc_d Thu 11:00, SQL Tuning 101, Moscone South, 102 sorry :-) ORA-03113 connormcdonald.wordpress.com 430