oracle直方圖
直方圖
當某列數據分布不均衡,為了讓CBO能生成最佳的執行計劃,我們可能需要對表收集直方圖,直方圖最大的桶數(Bucket)是254。
收集直方圖是一個很耗時的過程,如無必要,千萬別去收集直方圖。
Oracle的直方圖有兩種:
一種是頻率直方圖(FREQUENCY HISTOGRAM),當列中Distinct_keys 較少(小於254),如果不手工指定直方圖桶數(BUCKET),Oracle就會自動的創建頻率直方圖,並且桶數(BUCKET)等於Distinct_Keys。
一種是高度平衡直方圖(HEIGHT BALANCED),當列中Distinct_keys大於254,如果不手工指定直方圖桶數(BUCKET),Oracle就會自動的創建高度平衡直方圖。
直方圖用在什麼情況下?
列的值分布非常不均衡的時候,並且where條件中經常用到這個列。
直方圖都准嗎?
不一定。如果一個字段distinct值的個數非常多,基本接近主鍵的distinct值的個數,就沒必要做直方圖,直方圖也不一定100%准確。
相關的@腳本在文章的最後面提供。
SQL> drop table a;
表已刪除。
SQL> create table a as select * from dba_objects where rownum<=10000;
表已創建。
SQL> @anatab --常規的表分析
輸入 ownname 的值: ggs
輸入 tabname 的值: a
輸入 estimate_percent 的值: 100
輸入 skewonly_repeat_auto 的值: auto
輸入 degree 的值: 4
PL/SQL 過程已成功完成。
已用時間: 00: 00: 00.26
SQL> @getcolstat --字段的直方圖
輸入 owner 的值: ggs
輸入 table_name 的值: a
COLUMN_NAME NUM_ROWS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS LAST_ANALYZED
---------------- ---------- ----------- ----------- --------------------- --------------
SECONDARY 10000 1 .01 NONE 1 28-7月 -14
GENERATED 10000 2 .02 NONE 1 28-7月 -14
TEMPORARY 10000 2 .02 NONE 1 28-7月 -14
STATUS 10000 1 .01 NONE 1 28-7月 -14
TIMESTAMP 10000 350 3.5 NONE 1 28-7月 -14
LAST_DDL_TIME 10000 385 3.85 NONE 1 28-7月 -14
CREATED 10000 303 3.03 NONE 1 28-7月 -14
OBJECT_TYPE 10000 34 .34 NONE 1 28-7月 -14
DATA_OBJECT_ID 10000 1836 18.36 NONE 1 28-7月 -14
OBJECT_ID 10000 10000 100 NONE 1 28-7月 -14
SUBOBJECT_NAME 10000 27 .27 NONE 1 28-7月 -14
OBJECT_NAME 10000 7725 77.25 NONE 1 28-7月 -14
OWNER 10000 9 .09 NONE 1 28-7月 -14
已選擇13行。
SQL>
SQL> select object_type,count(*) from a group by object_type;
OBJECT_TYPE COUNT(*)
------------------- ----------
INDEX 946
JOB CLASS 2
CONTEXT 2
TYPE BODY 82
PROCEDURE 50
RESOURCE PLAN 3
RULE 1
SCHEDULE 1
TABLE PARTITION 52
WINDOW 2
WINDOW GROUP 1
TABLE 841
TYPE 1088
VIEW 2953
LIBRARY 113
FUNCTION 68
TRIGGER 5
PROGRAM 3
CLUSTER 10
SYNONYM 2458
PACKAGE BODY 470
QUEUE 21
CONSUMER GROUP 5
EVALUATION CONTEXT 8
RULE SET 11
DIRECTORY 2
UNDEFINED 6
OPERATOR 15
SEQUENCE 102
LOB 128
PACKAGE 485
JOB 6
INDEX PARTITION 59
LOB PARTITION 1
已選擇34行。
SQL> explain plan for select count(*) from a where object_type='INDEX';
已解釋。
SQL> @getplan
'general,outline,starts'
Enter value for plan type:general
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2223038180
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 25 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
|* 2 | TABLE ACCESS FULL| A | 294 | 2058 | 25 (0)| 00:00:01 | --跟實際不一致,上面查出來的是946
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_TYPE"='INDEX')
SQL> select 10000/34 from dual; --說明rows中的294是 估算值=總行數/字段distinct值的個數
10000/34
----------
294.117647
已選擇 1 行。
SQL>
SQL> @anatab_col
輸入 owner 的值: ggs
輸入 table_name 的值: a
輸入 columns 的值: object_type --做object_type字段的直方圖
PL/SQL 過程已成功完成。
SQL> explain plan for select count(*) from a where object_type='INDEX';
已解釋。
SQL> @getplan
'general,outline,starts'
Enter value for plan type:general
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2223038180
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 25 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
|* 2 | TABLE ACCESS FULL| A | 946 | 6622 | 25 (0)| 00:00:01 | --這裡返回的是真實的行數,做object_type字段的直方圖後,執行計劃非常准。
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_TYPE"='INDEX')
SQL>
SQL>
SQL>
SQL>
SQL> @getcolstat
輸入 owner 的值: ggs
輸入 table_name 的值: a
COLUMN_NAME NUM_ROWS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS LAST_ANALYZED
---------------- ---------- ----------- ----------- --------- ----------- --------------
SECONDARY 10000 1 .01 NONE 1 28-7月 -14
GENERATED 10000 2 .02 NONE 1 28-7月 -14
TEMPORARY 10000 2 .02 NONE 1 28-7月 -14
STATUS 10000 1 .01 NONE 1 28-7月 -14
TIMESTAMP 10000 350 3.5 NONE 1 28-7月 -14
LAST_DDL_TIME 10000 385 3.85 NONE 1 28-7月 -14
CREATED 10000 303 3.03 NONE 1 28-7月 -14
OBJECT_TYPE 10000 34 .34 FREQUENCY 34 28-7月 -14 --剛好等於distinct值
DATA_OBJECT_ID 10000 1836 18.36 NONE 1 28-7月 -14
OBJECT_ID 10000 10000 100 NONE 1 28-7月 -14
SUBOBJECT_NAME 10000 27 .27 NONE 1 28-7月 -14
OBJECT_NAME 10000 7725 77.25 NONE 1 28-7月 -14
OWNER 10000 9 .09 NONE 1 28-7月 -14
已選擇13行。
SQL> select count(distinct object_name) from a; --總共才10000行,可以看出object_name的選擇性是比較高的
COUNT(DISTINCTOBJECT_NAME)
--------------------------
7725
已選擇 1 行。
SQL> @anatab_col
輸入 owner 的值: ggs
輸入 table_name 的值: a
輸入 columns 的值: object_name
PL/SQL 過程已成功完成。
SQL> @getcolstat
輸入 owner 的值: ggs
輸入 table_name 的值: a
COLUMN_NAME NUM_ROWS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS LAST_ANALYZED
---------------- ---------- ----------- ----------- --------------- ----------- --------------
SECONDARY 10000 1 .01 NONE 1 28-7月 -14
GENERATED 10000 2 .02 NONE 1 28-7月 -14
TEMPORARY 10000 2 .02 NONE 1 28-7月 -14
STATUS 10000 1 .01 NONE 1 28-7月 -14
TIMESTAMP 10000 350 3.5 NONE 1 28-7月 -14
LAST_DDL_TIME 10000 385 3.85 NONE 1 28-7月 -14
CREATED 10000 303 3.03 NONE 1 28-7月 -14
OBJECT_TYPE 10000 34 .34 FREQUENCY 34 28-7月 -14
DATA_OBJECT_ID 10000 1836 18.36 NONE 1 28-7月 -14
OBJECT_ID 10000 10000 100 NONE 1 28-7月 -14
SUBOBJECT_NAME 10000 27 .27 NONE 1 28-7月 -14
OBJECT_NAME 10000 7725 77.25 HEIGHT BALANCED 75 28-7月 -14
OWNER 10000 9 .09 NONE 1 28-7月 -14
已選擇13行。
SQL> select count(*) from a where object_name like '%A%';
COUNT(*)
----------
6404
已選擇 1 行。
SQL> explain plan for select count(*) from a where object_name like '%A%';
已解釋。
SQL> @getplan
'general,outline,starts'
Enter value for plan type:general
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
Plan hash value: 2223038180
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 25 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS FULL| A | 500 | 9500 | 25 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_NAME" LIKE '%A%') --LIKE '%A%'對於cbo而言太復雜了,沒有真正跑的話,cbo根本不知道真正返回多少行。
SQL>
已選擇13行。
SQL> col OBJECT_NAME for a30
SQL> select OBJECT_NAME,count(*) from a group by OBJECT_NAME having count(*)>3 order by count(*) desc;
OBJECT_NAME COUNT(*)
------------------------------ ----------
DBMS_REPCAT_AUTH 5
已選擇 1 行。
SQL> explain plan for select count(*) from a where OBJECT_NAME='DBMS_REPCAT_AUTH';
已解釋。
SQL> @getplan
'general,outline,starts'
Enter value for plan type:general
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------
Plan hash value: 2223038180
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 25 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS FULL| A | 1 | 19 | 25 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_NAME"='DBMS_REPCAT_AUTH') --這個不復雜了吧,rows=1,一樣不准,直方圖也不可能保證100%准確的
所以說,並不是所有字段都適合做直方圖。distinct值非常多的,根本不適合做直方圖,默認的桶數也裝不下。
只有字段值傾斜非常嚴重,distinct值少,而且用到的sql中where條件包含了這個字段。如果sql中都沒有用到這個字段,那也沒必要做直方圖,
因為做直方圖是非常cpu性能的。
@腳本
--anatab.sql
set timing on
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => '&ownname',
tabname => '&tabname' ,
estimate_percent => &estimate_percent,
method_opt => 'for all columns size &skewonly_repeat_auto',
no_invalidate => FALSE,
degree => °ree,
cascade => TRUE);
END;
/
set timing off
--anatab_col.sql
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => '&owner',
tabname => '&table_name',
estimate_percent => 100,
method_opt => 'for columns &columns ', --such as:col1,col2,col3...
no_invalidate => FALSE,
degree => 4,
granularity => 'ALL',
cascade => TRUE);
END;
/
--getcolstat.sql
col COLUMN_NAME for a30
select a.column_name,
b.num_rows,
a.num_distinct Cardinality,
round(a.num_distinct / b.num_rows * 100, 2) selectivity,
a.histogram,
a.num_buckets,
a.last_analyzed
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner
and a.table_name = b.table_name
and a.owner = upper('&owner')
and a.table_name = upper('&table_name');
--getplan.sql
set feedback off
pro 'general,outline,starts'
pro
acc type prompt 'Enter value for plan type:' default 'general'
select * from table(dbms_xplan.display) where '&&type'='general';
select * from table(dbms_xplan.display(null, null,'advanced -projection')) where '&&type'='outline';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'ALLSTATS LAST')) where '&&type'='starts';
set feedback on
undef type