CBO中基數(cardinality)、可選擇率(selectivity)的計算公式

lhrbest發表於2017-06-06

CBO中基數(cardinality)、可選擇率(selectivity)的計算公式




Cardinality(基數)是Oracle預估的返回行數,即對目標SQL的某個具體執行步驟的執行結果所包含記錄數的估算值。如果是針對整個目標SQL,那麼此時的Cardinality就表示該SQL最終執行結果所包含記錄數的估算值。例如,一張表T1000行資料,列COL1上沒有直方圖,沒有空值,並且不重複的值(Distinct Value)有500個。那麼,在使用條件“WHERE COL1=”去訪問表的時候,優化器會假設資料均勻分佈,它估計出會有1000/500=2行被選出來,2就是這步操作的Cardinality。通常情況下,Cardinality越準確,生成的執行計劃就會越高效。

可選擇率(Selectivity)是指施加指定謂詞條件後返回結果集的記錄數佔未施加任何謂詞條件的原始結果集的記錄數的比率。可選擇率的取值範圍顯然是01,它的值越小,就表明可選擇性越好。可選擇率為1時的可選擇性是最差的。CBO就是用可選擇率來估算對應結果集的Cardinality可選擇率Cardinality之間的關係如下所示:

cardinality=NUM_ROWS*selectivity

其中,NUM_ROWS表示表的總行數。

Oracle資料庫中,Oracle會預設認為SQL語句的WHERE條件中出現的各列彼此之間是獨立的,是沒有關聯關係的。所以,如果目標SQL語句各列之間是以AND來組合的話,那麼該SQL語句整個WHERE條件的組合可選擇率就等於各個列各自施加查詢條件後可選擇率的乘積。在得到了SQL語句整個WHERE條件的組合可選擇率後,Oracle會用它來估算整個SQL語句返回結果集的Cardinality,估算的方法就是用目標表的總記錄數(NUM_ROWS)乘組合可選擇率。但Oracle預設認為的各列之間是獨立的、沒有關聯關係的前提條件並不總是正確的,在實際的應用中各列之間有關聯關係的情況實際上並不罕見。在這種情況下如果還用上述計算方法來計算目標SQL語句整個WHERE條件的組合可選擇率並用它來估算返回結果集的Cardinality的話,那麼估算結果可能就會與實際結果有較大的偏差,進而可能導致CBO選錯執行計劃,所以Oracle又引入了動態取樣和多列統計資訊

下表給出了一些常見的可選擇率計算公式:



下面給出一個示例:

DROP TABLE T_ROWS_20170605_LHR;

CREATE TABLE T_ROWS_20170605_LHR AS SELECT ROWNUM ID,'NAME1' SAL FROM DUAL CONNECT BY LEVEL<=10000;

UPDATE T_ROWS_20170605_LHR T SET T.ID='' WHERE T.ID<=100;

EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'T_ROWS_20170605_LHR',CASCADE=>TRUE,METHOD_OPT=>'FOR ALL COLUMNS SIZE 1',estimate_percent => 100);

LHR@orclasm > COL LOW_VALUE FORMAT A20

LHR@orclasm > COL HIGH_VALUE FORMAT A20

LHR@orclasm > SELECT D.LOW_VALUE,D.HIGH_VALUE,UTL_RAW.CAST_TO_NUMBER(D.LOW_VALUE) LOW_VALUE2,UTL_RAW.CAST_TO_NUMBER(D.HIGH_VALUE) HIGH_VALUE2, D.NUM_DISTINCT,D.NUM_NULLS FROM DBA_TAB_COL_STATISTICS D WHERE D.TABLE_NAME = 'T_ROWS_20170605_LHR' AND D.OWNER = 'LHR' AND D.COLUMN_NAME='ID';

LOW_VALUE            HIGH_VALUE           LOW_VALUE2 HIGH_VALUE2 NUM_DISTINCT  NUM_NULLS

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

C20202               C302                        101       10000         9900        100

LHR@orclasm > SELECT MIN(T.ID),DUMP(MIN(T.ID),16) LOW_VALUE,MAX(T.ID),DUMP(MAX(T.ID),16) HIGH_VALUE FROM  T_ROWS_20170605_LHR T;

 MIN(T.ID) LOW_VALUE             MAX(T.ID) HIGH_VALUE

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

       101 Typ=2 Len=3: c2,2,2       10000 Typ=2 Len=2: c3,2

下面分別執行如下4SQL語句並獲取執行計劃:

SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID=1000;--1

SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID>1000; --9000

SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID>=1000; --9001

SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID BETWEEN 1000 AND 1100; --101

LHR@orclasm > set autot on exp

LHR@orclasm > SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID=1000;

  COUNT(1)

----------

         1

Execution Plan

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

Plan hash value: 612708570

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

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

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

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

|   1 |  SORT AGGREGATE    |                     |     1 |     4 |            |          |

|*  2 |   TABLE ACCESS FULL| T_ROWS_20170605_LHR |     1 |     4 |     9   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - filter("T"."ID"=1000)

-- ROUND(NUM_ROWS*(1/NUM_DISTINCT)*((NUM_ROWS-NUM_NULLS)/NUM_ROWS))

LHR@orclasm > SELECT ROUND(10000*1/9900*((10000-100)/10000)) VALUE FROM DUAL;

     VALUE

----------

         1

LHR@orclasm > SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID>1000;

  COUNT(1)

----------

      9000

Execution Plan

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

Plan hash value: 612708570

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

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

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

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

|   1 |  SORT AGGREGATE    |                     |     1 |     4 |            |          |

|*  2 |   TABLE ACCESS FULL| T_ROWS_20170605_LHR |  9001 | 36004 |     9   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - filter("T"."ID">1000)

--ROUND(NUM_ROWS*((HIGH_VALUE-VAL)/(HIGH_VALUE-LOW_VALUE))*((NUM_ROWS-NUM_NULLS)/NUM_ROWS))

LHR@orclasm > SELECT ROUND(10000*((10000-1000)/(10000-101))*((10000-100)/10000)) VALUE FROM DUAL;

     VALUE

----------

      9001

LHR@orclasm > SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID>=1000;

  COUNT(1)

----------

      9001

Execution Plan

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

Plan hash value: 612708570

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

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

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

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

|   1 |  SORT AGGREGATE    |                     |     1 |     4 |            |          |

|*  2 |   TABLE ACCESS FULL| T_ROWS_20170605_LHR |  9002 | 36008 |     9   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - filter("T"."ID">=1000)

--ROUND(NUM_ROWS*((HIGH_VALUE-VAL)/(HIGH_VALUE-LOW_VALUE)+1/NUM_DISTINCT)*((NUM_ROWS-NUM_NULLS)/NUM_ROWS))

LHR@orclasm > SELECT ROUND(10000*((10000-1000)/(10000-101)+1/9900)*((10000-100)/10000)) VALUE FROM DUAL;

     VALUE

----------

      9002

LHR@orclasm > SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID BETWEEN 1000 AND 1100;

  COUNT(1)

----------

       101

Execution Plan

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

Plan hash value: 612708570

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

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

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

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

|   1 |  SORT AGGREGATE    |                     |     1 |     4 |            |          |

|*  2 |   TABLE ACCESS FULL| T_ROWS_20170605_LHR |   102 |   408 |     9   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - filter("T"."ID"<=1100 AND "T"."ID">=1000)

LHR@orclasm >

--ROUND(NUM_ROWS*(((VAL2-VAL1)/(HIGH_VALUE-LOW_VALUE)+2/NUM_DISTINCT)*((NUM_ROWS-NUM_NULLS)/NUM_ROWS)))

LHR@orclasm > SELECT ROUND(10000*(((1100-1000)/(10000-101)+2/9900)*((10000-100)/10000))) VALUE FROM DUAL;

     VALUE

----------

       102

可見預估行數和用公式計算出來的結果相吻合。

下面再檢視有頻率直方圖的時候基數的計算。

DROP TABLE T_ROWS_20170605_LHR;

CREATE TABLE T_ROWS_20170605_LHR AS SELECT ROWNUM ID,'NAME1' SAL FROM DUAL CONNECT BY LEVEL<=10000;

UPDATE T_ROWS_20170605_LHR T SET T.ID='' WHERE T.ID<=100;

UPDATE T_ROWS_20170605_LHR SET ID=2 WHERE ID BETWEEN 101 AND 200;

UPDATE T_ROWS_20170605_LHR SET ID=3 WHERE ID BETWEEN 200 AND 3000;

UPDATE T_ROWS_20170605_LHR SET ID=9 WHERE ID BETWEEN 3000 AND 9999;

SELECT T.ID,COUNT(*) FROM T_ROWS_20170605_LHR T GROUP BY T.ID;

檢視資料分佈:

LHR@orclasm > SELECT T.ID,COUNT(*) FROM T_ROWS_20170605_LHR T GROUP BY T.ID;

        ID   COUNT(*)

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

                  100

     10000          1

         2        100

         3       2800

         9       6999

收集頻率直方圖:

LHR@orclasm > EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'T_ROWS_20170605_LHR',CASCADE=>TRUE,METHOD_OPT=>'FOR COLUMNS ID SIZE 6',estimate_percent => 100);

PL/SQL procedure successfully completed.

LHR@orclasm > SELECT D.COLUMN_NAME,D.NUM_DISTINCT,D.NUM_NULLS,D.NUM_BUCKETS,D.HISTOGRAM,D.DENSITY FROM Dba_Tab_Columns D WHERE D.TABLE_NAME = 'T_ROWS_20170605_LHR' AND D.COLUMN_NAME='ID';

COLUMN_NAME                    NUM_DISTINCT  NUM_NULLS NUM_BUCKETS HISTOGRAM          DENSITY

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

ID                                        4        100           4 FREQUENCY       .000050505

LHR@orclasm > COL COLUMN_NAME FORMAT A6

LHR@orclasm > SELECT TABLE_NAME,COLUMN_NAME,ENDPOINT_NUMBER,ENDPOINT_VALUE,NVL((ENDPOINT_NUMBER-(LAG(ENDPOINT_NUMBER) OVER (ORDER BY ENDPOINT_VALUE))),ENDPOINT_NUMBER) COUNTS FROM DBA_TAB_HISTOGRAMS WHERE TABLE_NAME='T_ROWS_20170605_LHR' AND COLUMN_NAME='ID';

TABLE_NAME                     COLUMN ENDPOINT_NUMBER ENDPOINT_VALUE     COUNTS

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

T_ROWS_20170605_LHR            ID                 100              2        100

T_ROWS_20170605_LHR            ID                2900              3       2800

T_ROWS_20170605_LHR            ID                9899              9       6999

T_ROWS_20170605_LHR            ID                9900          10000          1

當目標列有頻率直方圖並且對目標列施加等值查詢條件時,如果查詢條件的輸入值等於目標列的某個BucketENDPOINT_VALUE,那麼cardinality=Current_ENDPOINT_NUMBER-Previous_ENDPOINT_NUMBER

LHR@orclasm > SET AUTOT ON

LHR@orclasm > SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID=3;

  COUNT(1)

----------

      2800

Execution Plan

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

Plan hash value: 612708570

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

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

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

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

|   1 |  SORT AGGREGATE    |                     |     1 |     3 |            |          |

|*  2 |   TABLE ACCESS FULL| T_ROWS_20170605_LHR |  2800 |  8400 |     9   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - filter("T"."ID"=3)

Statistics

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

          1  recursive calls

          0  db block gets

         23  consistent gets

          0  physical reads

          0  redo size

        526  bytes sent via SQL*Net to client

        519  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

可見,預估行數為2800,和直方圖中儲存的值吻合(2900-100)。

當目標列有頻率直方圖並且對目標列施加等值查詢條件時,如果查詢條件的輸入值不等於目標列的任意一個BucketENDPOINT_VALUE,那麼cardinality=MIN(Current_ENDPOINT_NUMBER-Previous_ENDPOINT_NUMBER)/2

LHR@orclasm > SELECT COUNT(1) FROM T_ROWS_20170605_LHR T WHERE T.ID=4;

  COUNT(1)

----------

         0

Execution Plan

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

Plan hash value: 612708570

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

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

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

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

|   1 |  SORT AGGREGATE    |                     |     1 |     3 |            |          |

|*  2 |   TABLE ACCESS FULL| T_ROWS_20170605_LHR |     1 |     3 |     9   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - filter("T"."ID"=4)

Statistics

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

          1  recursive calls

          0  db block gets

         23  consistent gets

          0  physical reads

          0  redo size

        525  bytes sent via SQL*Net to client

        519  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

LHR@orclasm > select round(1/2) from dual;

ROUND(1/2)

----------

         1

在直方圖中,由於MIN(Current_ENDPOINT_NUMBER-Previous_ENDPOINT_NUMBER)=1,所以,ROUND(1/2)=1,和執行計劃中的預估行數相吻合。





CBO優化器是基於對當前經過特定測試的資料集中預期的行比率估計來計算基數的。此處的行數之比是一個數值,稱為選擇率(selectivity)。得到選擇率之後,將其與輸入行數進行簡單相乘既可得到基數。

在理解選擇性之前,必須得對user_tab_col_statistics檢視有一定了解:

[sql] view plain copy
 print?
  1. SQL> desc user_tab_col_statistics  
  2.  名稱                                      是否為空? 型別  
  3.  ----------------------------------------- -------- ----------------------------  
  4.  TABLE_NAME                                         VARCHAR2(30)  表名  
  5.  COLUMN_NAME                                        VARCHAR2(30)  列名  
  6.  NUM_DISTINCT                                       NUMBER        列中distinct值的數目  
  7.  LOW_VALUE                                          RAW(32)       列的最小值  
  8.  HIGH_VALUE                                         RAW(32)       列的最大值  
  9.  DENSITY                                            NUMBER        當對列建立了直方圖,則值不再等於1/NUM_DISTINCT。  
  10.  NUM_NULLS                                          NUMBER        列中的NULL值數目。  
  11.  NUM_BUCKETS                                        NUMBER        Number of buckets in histogram for the column  
  12.  LAST_ANALYZED                                      DATE          最近分析時間。  
  13.  SAMPLE_SIZE                                        NUMBER        分析樣本大小。  
  14.  GLOBAL_STATS                                       VARCHAR2(3)   對分割槽取樣,則-NO,否則-YES。  
  15.  USER_STATS                                         VARCHAR2(3)   統計資訊由使用者匯入,則YES,否則-NO。  
  16.  AVG_COL_LEN                                        NUMBER        列的平均長度(以位元組為單位)  
  17.  HISTOGRAM                                          VARCHAR2(15)  Indicates existence/type of histogram: NONE、FREQUENCY、HEIGHT BALANCED  

下面建立一張測試表,並收集統計資訊:

[sql] view plain copy
 print?
  1. SQL> create table audience as  
  2.   2  select  
  3.   3    trunc(dbms_random.value(1,13))  month_no  
  4.   4  from  
  5.   5    all_objects  
  6.   6  where  
  7.   7    rownum <= 1200  
  8.   8  ;  
  9.   
  10. 表已建立。  
  11.   
  12. SQL> begin  
  13.   2    dbms_stats.gather_table_stats(  
  14.   3      user,  
  15.   4      'audience',  
  16.   5      cascade => true,  
  17.   6      estimate_percent => null,  
  18.   7     );ethod_opt => 'for all columns size 1'  
  19.     method_opt => 'for all columns size 1'  
  20.   8  );  
  21.   9  end;  
  22.  10  /  
  23.   
  24. PL/SQL 過程已成功完成。  

先檢視一下上面表和列的統計資訊:

[sql] view plain copy
 print?
  1. SQL> select t.TABLE_NAME, t.NUM_ROWS, t.BLOCKS, t.SAMPLE_SIZE  
  2.   2    from user_tables t;  
  3.   
  4. TABLE_NAME   NUM_ROWS     BLOCKS SAMPLE_SIZE  
  5. ---------- ---------- ---------- -----------  
  6. AUDIENCE         1200          5        1200  
  7.   
  8. SQL> select s.table_name,  
  9.        s.column_name,  
  10.        s.num_distinct,  
  11.   4         s.low_value,  
  12.        s.high_value,  
  13.        s.density,  
  14.   7         s.num_nulls,  
  15.   8         s.sample_size,  
  16.   9         s.avg_col_len  
  17.  10    from user_tab_col_statistics s;  
  18.   
  19. TABLE_NAME COLUMN_NAM NUM_DISTINCT LOW_VALUE  HIGH_VALUE    DENSITY  NUM_NULLS SAMPLE_SIZE AVG_COL_LEN  
  20. ---------- ---------- ------------ ---------- ---------- ---------- ---------- ----------- -----------  
  21. AUDIENCE   MONTH_NO             12 C102       C10D       .083333333          0        1200           3  
  22.   
  23. SQL> select rawtohex(1), rawtohex(12) from dual;  
  24.   
  25. RAWT RAWT  
  26. ---- ----  
  27. C102 C10D  
[sql] view plain copy
 print?
  1. SQL> select dump(1,16),dump(12,16) from dual;  
  2.    
  3. DUMP(1,16)        DUMP(12,16)  
  4. ----------------- -----------------  
  5. Typ=2 Len=2: c1,2 Typ=2 Len=2: c1,d  
  6.    
  7. SQL> select utl_raw.cast_to_number('c102'),utl_raw.cast_to_number('c10d'from dual;  
  8.    
  9. UTL_RAW.CAST_TO_NUMBER('C102') UTL_RAW.CAST_TO_NUMBER('C10D')  
  10. ------------------------------ ------------------------------  
  11.                              1                             12   --可以看見上面的LOW_VALUE和HIGH_VALUE的值分別就是1和12.  
[sql] view plain copy
 print?
  1. SQL> select count(a.month_no) from AUDIENCE a;  
  2.   
  3. COUNT(A.MONTH_NO)  
  4. -----------------  
  5.              1200     --可以看見,這裡的值和NUM_ROWS是一樣的。  
  6.   
  7. SQL> select count(distinct a.month_no) from AUDIENCE a;  
  8.   
  9. COUNT(DISTINCTA.MONTH_NO)  
  10. -------------------------  
  11.                        12       --可以看見,這裡的值也和NUM_DISTINCT的值是一樣的。  
[sql] view plain copy
 print?
  1. SQL> select 1/12 from dual;  
  2.   
  3.       1/12  
  4. ----------  
  5. .083333333               --這裡的值和DENSITY一樣的,計算公式為1/NUM_DISTINCT。  

 

1、假如在上面建立了一張表,裡面包含1200個人,如何才能確定其中有多少人的生日是在12月份。

[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no=12;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   100 |   300 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO"=12)  

可以看見CBO計算出1200裡面,12月份生日的人是100人(在ID=2行的rows)。這和我們通常所理解的是一樣的,我們知道月份只有12個,在1200人中在某一個月出生的人,算概率也是100人(CBO也是這樣做得)。

計算方法為:DENSITY * NUM_ROWS = 1 / 12 * 1200 = 100。

 

2、現在假設有10%的人不記得自己的生日了,那麼CBO會怎麼計算吶?

[sql] view plain copy
 print?
  1. SQL> drop table audience purge;  
  2.   
  3. 表已刪除。  
  4.   
  5. SQL> create table audience as  
  6.   2  select  
  7.   3    rownum        id,  
  8.   4    trunc(dbms_random.value(1,13))  month_no  
  9.   5  from  
  10.   6    all_objects  
  11.   7  where  
  12.   8    rownum <= 1200;  
  13.   
  14. 表已建立。  
  15.   
  16. SQL> update    
  17.   2    audience  
  18.   3  set  month_no = null  
  19.   4  where  mod(id,10) = 0;         --10%的使用者不記得自己的生日。  
  20.   
  21. 已更新120行。  
  22.   
  23. SQL> commit;  
  24.   
  25. 提交完成。  
  26.   
  27. SQL> begin  
  28.   2    dbms_stats.gather_table_stats(  
  29.   3      user,  
  30.   4      'audience',  
  31.   5      cascade => true,  
  32.   6      estimate_percent => null,  
  33.   7      method_opt => 'for all columns size 1'  
  34.   8    );  
  35.   9  end;  
  36.  10  /  
  37.   
  38. PL/SQL 過程已成功完成。  
  39.   
  40. SQL> select t.TABLE_NAME, t.NUM_ROWS, t.BLOCKS, t.SAMPLE_SIZE from user_tables t;  
  41.   
  42. TABLE_NAME   NUM_ROWS     BLOCKS SAMPLE_SIZE  
  43. ---------- ---------- ---------- -----------  
  44. AUDIENCE         1200          5        1200  
  45.   
  46. SQL> select s.table_name,  
  47.   2         s.column_name,  
  48.   3         s.num_distinct,  
  49.   4         s.low_value,  
  50.   5         s.high_value,  
  51.   6         s.density,  
  52.   7         s.num_nulls,  
  53.   8         s.sample_size,  
  54.   9         s.avg_col_len  
  55.  10    from user_tab_col_statistics s;  
  56.   
  57. TABLE_NAME COLUMN_NAM NUM_DISTINCT LOW_VALUE  HIGH_VALUE    DENSITY  NUM_NULLS SAMPLE_SIZE AVG_COL_LEN  
  58. ---------- ---------- ------------ ---------- ---------- ---------- ---------- ----------- -----------  
  59. AUDIENCE   MONTH_NO             12 C102       C10D       .083333333        120        1080           3   --這裡可以看見,NUM_NULLS的值確實為120。  
  60. AUDIENCE   ID                 1200 C102       C20D       .000833333          0        1200           4  
[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no=12;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |    90 |   270 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO"=12)  

調整後的選擇率:DENSITY * ((NUM_ROWS-NUM_NULLS)/NUM_ROWS) = 1 / 12 * ((1200 - 120) / 1200) = 0.075。

返回的記錄數(ROWS):調整後的選擇率 * NUM_ROWS = 0.075 * 1200 = 90行。

或者我們可以換一種方法思考,通過前面可以很容易的知道12分月有100人生日(其中這裡就包含了不記得生日的人)。然後1200人中有10%的人不記得自己的生日,也就是120,那麼12月份不記得自己生日的人就平攤到10個人,100-10=90。

3、現在假如我們想知道在6、7、8月份生日的人有多少吶?

[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no in(6,7,8);  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   270 |   810 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO"=6 OR "MONTH_NO"=7 OR "MONTH_NO"=8)  

6、7、8月份的選擇率:6月份選擇率 + 7月份選擇率 + 8月份選擇率 = 0.075 * 3 = 0.225

返回的記錄數(ROWS):6、7、8月份的選擇率 * NUM_ROWS = 0.225 * 1200 = 270行。

4、下面來一個更復雜一點的,我們想知道不在6、7、8月份生日的人有多少吶?

[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no not in(6,7,8);  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   674 |  2022 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO"<>6 AND "MONTH_NO"<>7 AND "MONTH_NO"<>8)  

選擇率:1 - 6、7、8月份的選擇率 = 1 - 0.075 * 3

返回記錄數:(1-0.075*3)*1200 = 930。

month_no in{specific list} 的基數 + month_no not in{specific list} 的基數 = NUM_ROWS,這裡計算出來是相等的,但是在資料庫中看見的卻不想等,需要注意!

 

5、現在我們求8月份以後出生的人,不包含8月份。

[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no>8;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   393 |  1179 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO">8)  

選擇率:((HIGH_VALUE - LIMIT) / (HIGH_VALUE - LOW_VALUE)) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS)

返回的記錄數:選擇率 * NUM_ROWS = ((HIGH_VALUE - LIMIT) / (HIGH_VALUE - LOW_VALUE)) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS) * NUM_ROWS = round(((12-8)/(12-1))*((1200-120)/1200)*1200) = 393。

如果是求8月份以後出生的人,包含8月份。

[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no>=8;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   483 |  1449 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO">=8)  

選擇率:((HIGH_VALUE - LIMIT) / (HIGH_VALUE - LOW_VALUE) + 1 / DENSITY) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS)

返回記錄數:選擇率 * NUM_ROWS = ((HIGH_VALUE - LIMIT) / (HIGH_VALUE - LOW_VALUE) + 1 / DENSITY) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS) * NUM_ROWS = round(((12-8)/(12-1)+1/12)*((1200-120)/1200)*1200) = 483。

如果是<8,選擇率:((LIMIT - LOW_VALUE) / (HIGH_VALUE - LOW_VALUE)) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS)

如果是<=8,選擇率:((LIMIT - LOW_VALUE) / (HIGH_VALUE - LOW_VALUE) + 1 / DENSITY) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS)


6、現在我們想知道6月份到8月份出生的人的數量?

[sql] view plain copy
 print?
  1. SQL> select count(*) from AUDIENCE where month_no>=6 and month_no<=8;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   376 |  1128 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO">=6 AND "MONTH_NO"<=8)  

選擇率:((HIGH_LIMIT - LOW_LIMIT) / (HIGH_VALUE - LOW_VALUE) + 1 / DENSITY + 1 / DENSITY) * ((NUM_ROWS - NUM_NULLS) / NUM_ROWS)

返回記錄數:round(((8-6)/(12-1)+1/12+1/12)*((1200-120)/1200)*1200) = 376。

7、下面看兩個謂詞的情況下,CBO是怎麼計算選擇率的。

[sql] view plain copy
 print?
  1. SQL> drop table audience purge;  
  2.   
  3. 表已刪除。  
  4.   
  5. SQL> create table audience as  
  6.   2  select  
  7.   3  rownum id,  
  8.   4  trunc(dbms_random.value(1,13))month_no,  
  9.   5  trunc(dbms_random.value(1,16))eu_country  
  10.   6  from  
  11.   7  all_objects  
  12.   8  where  
  13.   9  rownum <= 1200;  
  14.   
  15. 表已建立。  
  16.   
  17. SQL> begin  
  18.   2  dbms_stats.gather_table_stats(  
  19.   3  user,  
  20.   4  'audience',  
  21.   5  cascade => true,  
  22.   6  estimate_percent => null,  
  23.   7  method_opt => 'for all columns size 1'  
  24.   8  );  
  25.   9  end;  
  26.  10  /  
  27.   
  28. PL/SQL 過程已成功完成。  
  29.   
  30. SQL> select t.TABLE_NAME, t.NUM_ROWS, t.BLOCKS, t.SAMPLE_SIZE from user_tables t;  
  31.   
  32. TABLE_NAME   NUM_ROWS     BLOCKS SAMPLE_SIZE  
  33. ---------- ---------- ---------- -----------  
  34. AUDIENCE         1200          6        1200  
  35.   
  36. SQL> select s.table_name,  
  37.   2         s.column_name,  
  38.   3         s.num_distinct,  
  39.   4         s.low_value,  
  40.   5         s.high_value,  
  41.   6         s.density,  
  42.   7         s.num_nulls,  
  43.   8         s.sample_size,  
  44.   9         s.avg_col_len  
  45.  10    from user_tab_col_statistics s;  
  46.   
  47. TABLE_NAME COLUMN_NAM NUM_DISTINCT LOW_VALUE  HIGH_VALUE    DENSITY  NUM_NULLS SAMPLE_SIZE AVG_COL_LEN  
  48. ---------- ---------- ------------ ---------- ---------- ---------- ---------- ----------- -----------  
  49. AUDIENCE   EU_COUNTRY           15 C102       C110       .066666667          0        1200           3  
  50. AUDIENCE   MONTH_NO             12 C102       C10D       .083333333          0        1200           3  
  51. AUDIENCE   ID                 1200 C102       C20D       .000833333          0        1200           4  
[sql] view plain copy
 print?
  1. SQL> select count(*) from audience where month_no=12 and eu_country=8;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     6 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     6 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |     7 |    42 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("EU_COUNTRY"=8 AND "MONTH_NO"=12)  

選擇率:month_no選擇率 * eu_contry選擇率 = 1/12 * 1/15

返回記錄:round(1/12*1/15*1200) = 7。

[sql] view plain copy
 print?
  1. SQL> select count(*) from audience where month_no=12 or eu_country=8;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     6 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     6 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |   173 |  1038 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO"=12 OR "EU_COUNTRY"=8)  

選擇率:month_no選擇率 + eu_contry選擇率 - month_no選擇率 * eu_contry選擇率 = 1/12+1/15-1/12*1/15

返回記錄:round((1/12+1/15-1/12*1/15)*1200) = 173。

[sql] view plain copy
 print?
  1. SQL> select count(*) from audience where month_no<>12;  
  2.   
  3. 執行計劃  
  4. ----------------------------------------------------------  
  5. Plan hash value: 3337892515  
  6.   
  7. -------------------------------------------------------------------------------  
  8. | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
  9. -------------------------------------------------------------------------------  
  10. |   0 | SELECT STATEMENT   |          |     1 |     3 |     3   (0)| 00:00:01 |  
  11. |   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          |  
  12. |*  2 |   TABLE ACCESS FULL| AUDIENCE |  1100 |  3300 |     3   (0)| 00:00:01 |  
  13. -------------------------------------------------------------------------------  
  14.   
  15. Predicate Information (identified by operation id):  
  16. ---------------------------------------------------  
  17.   
  18.    2 - filter("MONTH_NO"<>12)  

選擇率:1- month_no選擇率 = 1- 1/12

返回記錄:(1-1/12)*1200 = 1100。


8、總結:

[sql] view plain copy
 print?
  1. 單個謂詞過濾:  
  2. =  基數計算公式 :1/num_distinct*(num_rows-num_nulls),如果有直方圖,基數計算公式=(num_rows-num_nulls)*density  
  3. >  基數計算公式:(high_value-limit)/(high_value-low_value)*(num_rows-num_nulls)  
  4. >= 基數計算公式:((high_value-limit)/(high_value-low_value)+1/num_distinct)*(num_rows-num_nulls)  因為有=,所以要加上=的選擇率,=的選擇率為1/num_distinct  
  5. <  基數計算公式:(limit-low_value)/(high_value-low_value)*(num_rows-num_nulls)  
  6. <= 基數計算公式:((limit-low_value)/(high_value-low_value)+1/num_distinct)*(num_rows-num_nulls)  
  7.   
  8. between ... and ... 的基數計算公式等價於 xxx<= high_limit ,xxxx>=low_limit   
  9. 基數計算公式:((high_limit-low_limit)/(high_value-low_value)+2/num_distinct)*(num_rows-num_nulls)  
  10. low_limit<xxx and</xxx  xxx<high_limit 基數計算公式:(high_limit-low_limit) (high_value-low_value)*(num_rows-num_nulls)  
  11. low_limit<=xxx and xxx<high_limit 基數計算公式:(high_limit-low_limit) (high_value-low_value)+1="" num_distinct)*(num_rows-num_nulls)  
  12.   
  13. 雙謂詞,多謂詞:  
  14. AND B 選擇率計算公式=A選擇率*B選擇率  
  15. OR B  選擇率計算公式=A+B-(A AND B)  
  16. NOT A   選擇率計算公式=1-A選擇率  





oracle包含的2種代價模型:

oracle通過隱含引數“_optimizer_cost_model"控制使用哪種代價模型

1io代價模型

cost=iocost

2cpu代價模型

cost=iocost+cpucost

在代價轉換的過程中,所有的代價都轉換為單資料塊讀的單次代價

存在四種型別的io,單塊讀,多塊讀,直接資料塊讀和直接資料塊寫

iocost=上述四種io的消耗

選擇率與密度,空值,唯一值,以及柱狀圖資料有關。過濾條件的選擇率

a and b a的選擇率*b的選擇率

a or b a的選擇率+b的選擇率-a*b

not a 1-a的選擇率

在未引入系統統計資訊之前,oracle會假設下面的2中情況是成立的:

1 單塊讀的平均消耗時間和多塊讀的平均消耗時間相等

2全表掃的時候,根據下面的情況計算全表掃描的成本

mbdivisor=1.675*power(db_file_multiblock_read_count,0.6581)

tsc=blocks/mbdivisor

10.2中的mbdivisor大約是4.4,11.2中的改值大約是3.6

cpu cost model在開啟的情況下,全表掃描的計算公式如下:

tsc cost=i/o cost+cpu cost

i/o cost =1+ceil(blocks/mbrc)*(mreadtim/sreadtim)

cpu cost=round(#cpucycles/cpuspeed/1000/sreadtim)

系統的統計資訊放在了aux_stats$中

檢視統計資訊收集的歷史

wri$_optstat_tab_history

執行計劃中的記錄數是資料記錄數*選擇率,執行時間的估算,總的單資料塊讀的io代價*單次資料塊讀時間

全表掃描的大概估計:

索引範圍掃描:分支節點讀取的資料塊數和葉子節點資料塊數決定。分支節點資料塊的讀取數等於索引數的分支節點層數。葉子節點的計算是葉子節點數*條件選擇率

所以索引的代價大概是層數+葉結點數*選擇率*optimizer_index_cost_adj

索引rowid訪問表的計算:這邊涉及到一個聚簇因子,它是代表了資料塊的聚集性。

iocost=索引樹高度+葉子資料塊數量*access的選擇率+聚簇因子*filter的選擇率*optica/100


索引範圍掃描的計算公式:

i/o cost= index access i/o cost + table access i/o cost

index access i/o cost= blevel+ceil(#leaf_block*ix_sel)

table acess i/o cost=ceil(clustering_factor*ix_sel_with_filters)


列上沒有直方圖,且沒有null值得情況下,列的選擇率=(1/num_distinct)

列上沒有直方圖,且有null值的情況下,列的選擇率=(1/num_distinct)*((num_rows-num_nulls)/num_rows)

列上的low_value和high_value是用來評估列做範圍查詢時的可選擇率

目標列大於指定值val,且val處於low_value與high_value之間

selectivity=((high_value-val)/(high_value-low_value)*null_adjust;

null_adjust=(num_rows-num_nulls)/num_rows

目標列小於指定val,且val處於low和high之間

selectivity=(val-low_value)/(high_value-low_value)*null_adjust;

null_adjust=(num_rows-num_nulls)/num_rows

目標列大於或等於指定值val,且val處於low和high之間

selectivity=((high-val)/(high-low)+1/num_distinct)*null_adjust

目標列小於或等於指定值val,且val處於low和high之間

selectivity=((val-low)/high-low)+1/num_distinct)*null_adjust

目標列在指定值val1和val2之間,且val1和val2都在low和high之間

selectivity=((val2-val1)/high-low)+2/num_distinct)*null_adjust

對於使用了繫結變數的sql,啟動繫結變數窺探,則對於>的範圍查詢

Selectivity = (high_value - limit) / (high_value - low_value)  limit是繫結變數的值

Cardinality = rows*selectivity

對於使用了繫結變數的sql,如果沒有啟用繫結變數窺探,則對於>的範圍查詢

selectivity=0.05

沒有啟用繫結變數窺探,對於between and的查詢

selectivity=0.0025



--測試選擇率

SQL> select count(*) from tt where object_id is null;

  COUNT(*)
----------
        99

SQL> select count(*) from tt;

  COUNT(*)
----------
     49859

 exec dbms_stats.gather_table_stats(ownname => 'baixyu',tabname => 'TT',method_opt => 'for all columns size 1');

SQL> select count(*) from tt where object_id is null;


  COUNT(*)
----------
        99


SQL> select count(*) from tt;


  COUNT(*)
----------
     49859

 select num_distinct,low_value,high_value,num_nulls from user_tab_col_statistics s where s.table_name='TT' and s.column_name='OBJECT_ID';


49760 C2020BC3061C2299



SQL> select * from tt where object_id=120;




執行計劃
----------------------------------------------------------
Plan hash value: 264906180


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    93 |   159   (2)| 00:00:02 |
|*  1 |  TABLE ACCESS FULL| TT   |     1 |    93 |   159   (2)| 00:00:02 |
--------------------------------------------------------------------------


SQL> select 1/49760*((49859-99)/49859)*49859 from dual;


1/49760*((49859-99)/49859)*49859
--------------------------------
                               1

SQL> select * from tt where object_id>120 and object_id<200;


已選擇78行。




執行計劃
----------------------------------------------------------
Plan hash value: 264906180


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    76 |  7068 |   159   (2)| 00:00:02 |
|*  1 |  TABLE ACCESS FULL| TT   |    76 |  7068 |   159   (2)| 00:00:02 |
--------------------------------------------------------------------------


select  (((200-120)/(52744-101)+2/49760)*49760/49859)*49859 from dual;

(((200-120)/(52744-101)+2/49760)*49760/49859)*49859
---------------------------------------------------
                                         77.6187907


在列上沒有直方圖的情況下,統計資訊中的density可以認為是1/num_distinct,在列上有等頻直方圖時,density的計算公式為

density=1/(2*num_rows*null_adjust)

null_adjust-(num_rows-num_nulls)/num_rows

如果查詢條件的輸入值等於目標列的某個bucket的endpoint_value那麼 列的cardinality的計算公式是

cardinality=num_rows*selectivity

selectivity=bucketsize/num_rows

bucketsize=current_endpoint_number-previous_endpoint_number

如果查詢條件的輸入值不等於目標列的任意一個bucket的endpoint_value,計算公式如下

cardinality=num_rows*selectivity

selectivity=min(bucketsize)/(2*num_rows)

bucketsize=current_endpoint_number-previous_endpoint_number;

---怎麼不直接設定成0?

當列上的直方圖資訊是等高直方圖的時候,等值查詢的估算是

1如果查詢條件輸入值是popular value

cardinality=num_rows*selectivity

selectivity=(buckets_this_popular_value/buckets_totoal)*null_adjust

null_adjust=(num_rows-num_nulls)/num_rows

2如果是unpopular value

分成三種情況。。。。



本章將討論採用標準的選擇率計算方式卻產生不合適結果的一些最常見原因。

1、前導零

建立一個包含2000000行資料的表,它有一個id列,採用序號並利用0來進行填充其他位。因此典型的儲存值將是A00000000000000001,系統中絕大部分使用這種策略的查詢,類似於where id={string constant}的形式;但是,如果它們使用了基於區間的謂詞,可能將出現一些奇怪的效能問題。

[sql] view plain copy
 print?
  1. SQL> create table t1   
  2.   2  nologging  
  3.   3  pctfree 0  
  4.   4  as  
  5.   5  with generator as (  
  6.   6    select  
  7.   7      rownum   id  
  8.   8    from  all_objects   
  9.   9    where  rownum <= 2000  
  10.  10  )  
  11.  11  select  
  12.  12    /*+ ordered use_nl(v2) */  
  13.  13    trunc((rownum-1)/10000)    grp_id,  
  14.  14    'A' || lpad(rownum, 17, '0')  id  
  15.  15  from  
  16.  16    generator  v1,  
  17.  17    generator  v2  
  18.  18  where  
  19.  19    rownum <= 2000000  
  20.  20  ;  
  21.   
  22. 表已建立。  
  23.   
  24. SQL> begin  
  25.   2  dbms_stats.gather_table_stats(  
  26.   3  ownname=> user,  
  27.   4  tabname=> 't1',  
  28.   5  cascade=> true,  
  29.   6  estimate_percent=> null,   
  30.   7  method_opt=>'for all columns size 1'  
  31.   8  );  
  32.   9  end;  
  33.  10  /  
  34.   
  35. PL/SQL 過程已成功完成。  
[sql] view plain copy
 print?
  1. SQL> set autotrace traceonly;  
  2. SQL> select * from t1 where id between 'A00000000000060000' and 'A00000000000070000';  
  3.   
  4. 已選擇10001行。  
  5.   
  6.   
  7. 執行計劃  
  8. ----------------------------------------------------------  
  9. Plan hash value: 3617692013  
  10.   
  11. --------------------------------------------------------------------------  
  12. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  13. --------------------------------------------------------------------------  
  14. |   0 | SELECT STATEMENT  |      |    17 |   374 |  1190   (3)| 00:00:15 |  
  15. |*  1 |  TABLE ACCESS FULL| T1   |    17 |   374 |  1190   (3)| 00:00:15 |  
  16. --------------------------------------------------------------------------  
  17.   
  18. Predicate Information (identified by operation id):  
  19. ---------------------------------------------------  
  20.   
  21.    1 - filter("ID"<='A00000000000070000' AND "ID">='A00000000000060000')  
  22.   
  23.   
  24. 統計資訊  
  25. ----------------------------------------------------------  
  26.           1  recursive calls  
  27.           0  db block gets  
  28.        7520  consistent gets  
  29.        6849  physical reads  
  30.           0  redo size  
  31.      325111  bytes sent via SQL*Net to client  
  32.        7741  bytes received via SQL*Net from client  
  33.         668  SQL*Net roundtrips to/from client  
  34.           0  sorts (memory)  
  35.           0  sorts (disk)  
  36.       10001  rows processed  

針對該查詢得到的基數是非常低的(17),但是很明顯卻返回了10001行。下面重新建立直方圖,直方圖的預設值為75個桶。

[sql] view plain copy
 print?
  1. SQL> set autotrace off;  
  2. SQL> begin  
  3.   2  dbms_stats.gather_table_stats(  
  4.   3  ownname=> user,  
  5.   4  tabname=> 't1',  
  6.   5  cascade=> true,  
  7.   6  estimate_percent=> null,   
  8.   7  method_opt=>'for all columns size 75'  
  9.   8  );  
  10.   9  end;  
  11.  10  /  
  12.   
  13. PL/SQL 過程已成功完成。  
  14.   
  15. SQL> set autotrace traceonly;  
  16. SQL> select * from t1 where id between 'A00000000000060000' and 'A00000000000070000';  
  17.   
  18. 已選擇10001行。  
  19.   
  20.   
  21. 執行計劃  
  22. ----------------------------------------------------------  
  23. Plan hash value: 3617692013  
  24.   
  25. --------------------------------------------------------------------------  
  26. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  27. --------------------------------------------------------------------------  
  28. |   0 | SELECT STATEMENT  |      |  8924 |   191K|  1190   (3)| 00:00:15 |  
  29. |*  1 |  TABLE ACCESS FULL| T1   |  8924 |   191K|  1190   (3)| 00:00:15 |  
  30. --------------------------------------------------------------------------  
  31.   
  32. Predicate Information (identified by operation id):  
  33. ---------------------------------------------------  
  34.   
  35.    1 - filter("ID"<='A00000000000070000' AND "ID">='A00000000000060000')  
  36.   
  37.   
  38. 統計資訊  
  39. ----------------------------------------------------------  
  40.           0  recursive calls  
  41.           0  db block gets  
  42.        7520  consistent gets  
  43.        6849  physical reads  
  44.           0  redo size  
  45.      325111  bytes sent via SQL*Net to client  
  46.        7741  bytes received via SQL*Net from client  
  47.         668  SQL*Net roundtrips to/from client  
  48.           0  sorts (memory)  
  49.           0  sorts (disk)  
  50.       10001  rows processed  

直方圖的預設值為75個桶,執行計劃顯示估計的基數為8924——這個結果至少大體上是正確的。建立直方圖是一種方法,如果id列的值沒有字元,是可以轉換為數字的字串(00000000000000001),那麼在id列上面建立一個函式索引也是一種辦法:create index t1_i1 on t1(grp_id, to_number(id));

2、致命的預設值

即時是資料庫應用程式正確的使用了日期型別,也仍然需要避免null值。為了不讓任何列為null,每個可空的列都有一個預設值。因此,大部分獨立於資料庫的開發人員會選擇一個什麼樣的值來表示null日期呢?如果表示很久以後的日期呢?比如4000年12月31日。

[sql] view plain copy
 print?
  1. SQL> create table t1   
  2.   2  as  
  3.   3  with generator as (  
  4.   4  select  
  5.   5  rownum id  
  6.   6  from all_objects   
  7.   7  where rownum <= 2000  
  8.   8  )  
  9.   9  select  
  10.  10  /*+ ordered use_nl(v2) */  
  11.  11  decode(  
  12.  12      mod(rownum - 1,1000),  
  13.  13        0,to_date('4000-12-31','yyyy-mm-dd'),  
  14.  14          to_date('2000-01-01','yyyy-mm-dd') + trunc((rownum - 1)/100)   
  15.  15    )  date_closed  
  16.  16  from  
  17.  17    generator  v1,  
  18.  18    generator  v2  
  19.  19  where rownum <= 1827 * 100;  
  20.   
  21. 表已建立。  
  22.   
  23. SQL> begin  
  24.   2  dbms_stats.gather_table_stats(  
  25.   3  ownname=> user,  
  26.   4  tabname=> 't1',  
  27.   5  cascade=> true,  
  28.   6  estimate_percent=> null,   
  29.   7  method_opt=>'for all columns size 1'    --直方圖的預設值為1桶。  
  30.   8  );  
  31.   9  end;  
  32.  10  /  
  33.   
  34. PL/SQL 過程已成功完成。  
  35.   
  36. SQL> set autotrace traceonly;  
  37. SQL> select *  
  38.   2    from t1  
  39.   3   where date_closed between to_date('2003-01-01''yyyy-mm-dd'and  
  40.   4         to_date('2003-12-31''yyyy-mm-dd');  
  41.   
  42. 已選擇36463行。  
  43.   
  44.   
  45. 執行計劃  
  46. ----------------------------------------------------------  
  47. Plan hash value: 3617692013  
  48.   
  49. --------------------------------------------------------------------------  
  50. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  51. --------------------------------------------------------------------------  
  52. |   0 | SELECT STATEMENT  |      |   291 |  2328 |    61   (4)| 00:00:01 |  
  53. |*  1 |  TABLE ACCESS FULL| T1   |   291 |  2328 |    61   (4)| 00:00:01 |  
  54. --------------------------------------------------------------------------  
  55.   
  56. Predicate Information (identified by operation id):  
  57. ---------------------------------------------------  
  58.   
  59.    1 - filter("DATE_CLOSED"<=TO_DATE(' 2003-12-31 00:00:00',  
  60.               'syyyy-mm-dd hh24:mi:ss'AND "DATE_CLOSED">=TO_DATE(' 2003-01-01  
  61.               00:00:00', 'syyyy-mm-dd hh24:mi:ss'))  
  62.   
  63.   
  64. 統計資訊  
  65. ----------------------------------------------------------  
  66.           1  recursive calls  
  67.           0  db block gets  
  68.        2759  consistent gets  
  69.           0  physical reads  
  70.           0  redo size  
  71.      494301  bytes sent via SQL*Net to client  
  72.       27145  bytes received via SQL*Net from client  
  73.        2432  SQL*Net roundtrips to/from client  
  74.           0  sorts (memory)  
  75.           0  sorts (disk)  
  76.       36463  rows processed     --這裡實際放回了36463行記錄,但是oracle卻計算錯誤了(291)。  
  77.   
  78. SQL> set autotrace off;  
  79. SQL> begin  
  80.   2  dbms_stats.gather_table_stats(  
  81.   3  ownname=> user,  
  82.   4  tabname=> 't1',  
  83.   5  cascade=> true,  
  84.   6  estimate_percent=> null,   
  85.   7  method_opt=>'for all columns size 11'    --直方圖的預設值為11桶。  
  86.   8  );  
  87.   9  end;  
  88.  10  /  
  89.   
  90. PL/SQL 過程已成功完成。  
  91.   
  92. SQL> set autotrace traceonly;  
  93. SQL> select *  
  94.   2    from t1  
  95.   3   where date_closed between to_date('2003-01-01''yyyy-mm-dd'and  
  96.   4         to_date('2003-12-31''yyyy-mm-dd');  
  97.   
  98. 已選擇36463行。  
  99.   
  100.   
  101. 執行計劃  
  102. ----------------------------------------------------------  
  103. Plan hash value: 3617692013  
  104.   
  105. --------------------------------------------------------------------------  
  106. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  107. --------------------------------------------------------------------------  
  108. |   0 | SELECT STATEMENT  |      | 36320 |   283K|    61   (4)| 00:00:01 |  
  109. |*  1 |  TABLE ACCESS FULL| T1   | 36320 |   283K|    61   (4)| 00:00:01 |  
  110. --------------------------------------------------------------------------  
  111.   
  112. Predicate Information (identified by operation id):  
  113. ---------------------------------------------------  
  114.   
  115.    1 - filter("DATE_CLOSED">=TO_DATE(' 2003-01-01 00:00:00',  
  116.               'syyyy-mm-dd hh24:mi:ss'AND "DATE_CLOSED"<=TO_DATE(' 2003-12-31  
  117.               00:00:00', 'syyyy-mm-dd hh24:mi:ss'))  
  118.   
  119.   
  120. 統計資訊  
  121. ----------------------------------------------------------  
  122.           0  recursive calls  
  123.           0  db block gets  
  124.        2759  consistent gets  
  125.           0  physical reads  
  126.           0  redo size  
  127.      494301  bytes sent via SQL*Net to client  
  128.       27145  bytes received via SQL*Net from client  
  129.        2432  SQL*Net roundtrips to/from client  
  130.           0  sorts (memory)  
  131.           0  sorts (disk)  
  132.       36463  rows processed          --36463:36320這次oracle計算得比較準確了。  

可以看見加大了直方圖的桶數之後,CBO估算的行數就比較接近真實值了,那我們再加大直方圖的桶數試一下吶!

[sql] view plain copy
 print?
  1. SQL> set autotrace off;  
  2. SQL> begin  
  3.   2  dbms_stats.gather_table_stats(  
  4.   3  ownname=> user,  
  5.   4  tabname=> 't1',  
  6.   5  cascade=> true,  
  7.   6  estimate_percent=> null,   
  8.   7  method_opt=>'for all columns size 75'  
  9.   8  );  
  10.   9  end;  
  11.  10  /  
  12.   
  13. PL/SQL 過程已成功完成。  
  14.   
  15. SQL> set autotrace trace;  
  16. SQL> select *  
  17.   2    from t1  
  18.   3   where date_closed between to_date('2003-01-01''yyyy-mm-dd'and  
  19.   4         to_date('2003-12-31''yyyy-mm-dd');  
  20.   
  21. 已選擇36463行。  
  22.   
  23.   
  24. 執行計劃  
  25. ----------------------------------------------------------  
  26. Plan hash value: 3617692013  
  27.   
  28. --------------------------------------------------------------------------  
  29. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  30. --------------------------------------------------------------------------  
  31. |   0 | SELECT STATEMENT  |      | 36345 |   283K|    61   (4)| 00:00:01 |  
  32. |*  1 |  TABLE ACCESS FULL| T1   | 36345 |   283K|    61   (4)| 00:00:01 |  
  33. --------------------------------------------------------------------------  
  34.   
  35. Predicate Information (identified by operation id):  
  36. ---------------------------------------------------  
  37.   
  38.    1 - filter("DATE_CLOSED">=TO_DATE(' 2003-01-01 00:00:00',  
  39.               'syyyy-mm-dd hh24:mi:ss'AND "DATE_CLOSED"<=TO_DATE(' 2003-12-31  
  40.               00:00:00', 'syyyy-mm-dd hh24:mi:ss'))  
  41.   
  42.   
  43. 統計資訊  
  44. ----------------------------------------------------------  
  45.           0  recursive calls  
  46.           0  db block gets  
  47.        2759  consistent gets  
  48.           0  physical reads  
  49.           0  redo size  
  50.      494301  bytes sent via SQL*Net to client  
  51.       27145  bytes received via SQL*Net from client  
  52.        2432  SQL*Net roundtrips to/from client  
  53.           0  sorts (memory)  
  54.           0  sorts (disk)  
  55.       36463  rows processed    --36463:36345 加大了直方圖的桶數之後,CBO估算返回的行數誤差更小了。  

3、離散資料的風險

考慮一個包含period列的計數系統——儲存1~12月的資料,並額外再加一個月,其對應的數值為99(同時包含了第二種選擇,即這個特殊的月給定的值為13)。

[sql] view plain copy
 print?
  1. SQL> create table t1   
  2.   2  as  
  3.   3  with generator as (  
  4.   4  select  
  5.   5      rownum   id  
  6.   6    from  all_objects   
  7.   7    where  rownum <= 1000  
  8.   8  )  
  9.   9  select  
  10.  10    /*+ ordered use_nl(v2) */  
  11.  11    mod(rownum-1,13)  period_01,   
  12.  12    mod(rownum-1,13)  period_02  
  13.  13  from  
  14.  14    generator  v1,  
  15.  15    generator  v2  
  16.  16  where  
  17.  17    rownum <= 13000  
  18.  18  ;  
  19.   
  20. 表已建立。  
  21.   
  22. SQL> update t1 set   
  23.   2  period_01 = 99,  
  24.   3  period_02 = 13  
  25.   4  where   
  26.   5  period_01 = 0;  
  27.   
  28. 已更新1000行。  
  29.   
  30. SQL> commit;  
  31.   
  32. 提交完成。  
  33.   
  34. SQL> begin  
  35.   2  dbms_stats.gather_table_stats(  
  36.   3  user,  
  37.   4  't1',  
  38.   5  cascade => true,  
  39.   6  estimate_percent => null,  
  40.   7  method_opt => 'for all columns size 1'  
  41.   8  );  
  42.   9  end;  
  43.  10  /  
  44.   
  45. PL/SQL 過程已成功完成。  
  46.   
  47. SQL> set autotrace on exp;  
  48. SQL> select count(*) from t1 where period_01 between 4 and 6;  
  49.   
  50.   COUNT(*)  
  51. ----------  
  52.       3000          --這裡實際有3000行,但是oracle估算的是1663行,不準確!  
  53.   
  54.   
  55. 執行計劃  
  56. ----------------------------------------------------------  
  57. Plan hash value: 3724264953  
  58.   
  59. ---------------------------------------------------------------------------  
  60. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  61. ---------------------------------------------------------------------------  
  62. |   0 | SELECT STATEMENT   |      |     1 |     3 |     6   (0)| 00:00:01 |  
  63. |   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |  
  64. |*  2 |   TABLE ACCESS FULL| T1   |  1663 |  4989 |     6   (0)| 00:00:01 |  
  65. ---------------------------------------------------------------------------  
  66.   
  67. Predicate Information (identified by operation id):  
  68. ---------------------------------------------------  
  69.   
  70.    2 - filter("PERIOD_01"<=6 AND "PERIOD_01">=4)  
  71.   
  72. SQL> select count(*) from t1 where period_02 between 4 and 6;  
  73.   
  74.   COUNT(*)  
  75. ----------  
  76.       3000       --這裡實際有3000行,但是oracle估算的是4167行,不準確!  
  77.   
  78.   
  79. 執行計劃  
  80. ----------------------------------------------------------  
  81. Plan hash value: 3724264953  
  82.   
  83. ---------------------------------------------------------------------------  
  84. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  85. ---------------------------------------------------------------------------  
  86. |   0 | SELECT STATEMENT   |      |     1 |     3 |     6   (0)| 00:00:01 |  
  87. |   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |  
  88. |*  2 |   TABLE ACCESS FULL| T1   |  4167 | 12501 |     6   (0)| 00:00:01 |  
  89. ---------------------------------------------------------------------------  
  90.   
  91. Predicate Information (identified by operation id):  
  92. ---------------------------------------------------  
  93.   
  94.    2 - filter("PERIOD_02"<=6 AND "PERIOD_02">=4)  
  95.   
  96. SQL> set autotrace off;  
  97. SQL> begin  
  98.   2  dbms_stats.gather_table_stats(  
  99.   3  user,  
  100.   4  't1',  
  101.   5  cascade => true,  
  102.   6  estimate_percent => null,  
  103.   7  method_opt => 'for all columns size 254'           --重新收集直方圖。  
  104.   8  );  
  105.   9  end;  
  106.  10  /  
  107.   
  108. PL/SQL 過程已成功完成。  
  109.   
  110. SQL> set autotrace on exp;  
  111. SQL> select count(*) from t1 where period_01 between 4 and 6;  
  112.   
  113.   COUNT(*)  
  114. ----------  
  115.       3000  
  116.   
  117.   
  118. 執行計劃  
  119. ----------------------------------------------------------  
  120. Plan hash value: 3724264953  
  121.   
  122. ---------------------------------------------------------------------------  
  123. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  124. ---------------------------------------------------------------------------  
  125. |   0 | SELECT STATEMENT   |      |     1 |     3 |     6   (0)| 00:00:01 |  
  126. |   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |  
  127. |*  2 |   TABLE ACCESS FULL| T1   |  3000 |  9000 |     6   (0)| 00:00:01 |  
  128. ---------------------------------------------------------------------------  
  129.   
  130. Predicate Information (identified by operation id):  
  131. ---------------------------------------------------  
  132.   
  133.    2 - filter("PERIOD_01"<=6 AND "PERIOD_01">=4)  
  134.   
  135. SQL> select count(*) from t1 where period_02 between 4 and 6;  
  136.   
  137.   COUNT(*)  
  138. ----------  
  139.       3000  
  140.   
  141.   
  142. 執行計劃  
  143. ----------------------------------------------------------  
  144. Plan hash value: 3724264953  
  145.   
  146. ---------------------------------------------------------------------------  
  147. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  148. ---------------------------------------------------------------------------  
  149. |   0 | SELECT STATEMENT   |      |     1 |     3 |     6   (0)| 00:00:01 |  
  150. |   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |  
  151. |*  2 |   TABLE ACCESS FULL| T1   |  3000 |  9000 |     6   (0)| 00:00:01 |  
  152. ---------------------------------------------------------------------------  
  153.   
  154. Predicate Information (identified by operation id):  
  155. ---------------------------------------------------  
  156.   
  157.    2 - filter("PERIOD_02"<=6 AND "PERIOD_02">=4)  

4、函式索引

[sql] view plain copy
 print?
  1. SQL> create index idx_t1_PERIOD_01 on t1(upper(PERIOD_01));  
  2.   
  3. 索引已建立。  
  4.   
  5. SQL> select i.index_name,i.index_type from user_indexes i;  
  6.   
  7. INDEX_NAME                     INDEX_TYPE  
  8. ------------------------------ ---------------------------  
  9. IDX_T1_PERIOD_01               FUNCTION-BASED NORMAL  
  10.   
  11. SQL> select c.TABLE_NAME,c.COLUMN_NAME from user_tab_cols c;  
  12.   
  13. TABLE_NAME                     COLUMN_NAME  
  14. ------------------------------ ------------------------------  
  15. T1                             PERIOD_01  
  16. T1                             PERIOD_02  
  17. T1                             SYS_NC00003$  

需要牢記的是,如果建立了一個基於函式的索引,那麼實際上是在虛擬列上建立了索引,當收集關於該表及其索引的統計資訊時,同時也就收集了虛擬列上的統計資訊。這在類似情況下,諸如upper(PERIOD_01)='xxx'的謂詞將被優化為:SYS_NC00003$='xxx'。

5、相互關聯的列

如果在謂詞中使用相互依賴(相關)的列容易將問題複雜化,前提是這些相關聯的列同時出現在where子句中。

[sql] view plain copy
 print?
  1. SQL> create table t1   
  2.   2  nologging  
  3.   3  as  
  4.   4  select  
  5.   5  trunc(dbms_random.value(0,25))n1,  
  6.   6  rpad('x',40)ind_pad,  
  7.   7  trunc(dbms_random.value(0,20))n2,  
  8.   8  lpad(rownum,10,'0')small_vc,  
  9.   9  rpad('x',200)padding  
  10.  10  from  
  11.  11  all_objects  
  12.  12  where  
  13.  13  rownum  <= 10000  
  14.  14  ;  
  15.   
  16. 表已建立。  
  17.   
  18. SQL> update t1 set n2 = n1;  
  19.   
  20. 已更新10000行。  
  21.   
  22. SQL> commit;  
  23.   
  24. 提交完成。  
  25.   
  26. SQL> begin  
  27.   2  dbms_stats.gather_table_stats(  
  28.   3  ownname => user,  
  29.   4  tabname=> 'T1',  
  30.   5  cascade=> true,  
  31.   6  estimate_percent => null,  
  32.   7  method_opt => 'for all columns size 1'  
  33.   8  );  
  34.   9  end;  
  35.  10  /  
  36.   
  37. PL/SQL 過程已成功完成。  
  38.   
  39. SQL> set autotrace traceonly;  
  40. SQL> select small_vc  
  41.   2    from t1  
  42.   3   where n1 = 2  
  43.   4     and n2 = 2;  
  44.   
  45. 已選擇420行。  
  46.   
  47.   
  48. 執行計劃  
  49. ----------------------------------------------------------  
  50. Plan hash value: 3617692013  
  51.   
  52. --------------------------------------------------------------------------  
  53. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  54. --------------------------------------------------------------------------  
  55. |   0 | SELECT STATEMENT  |      |    16 |   272 |    66   (0)| 00:00:01 |  
  56. |*  1 |  TABLE ACCESS FULL| T1   |    16 |   272 |    66   (0)| 00:00:01 |  
  57. --------------------------------------------------------------------------  
  58.   
  59. Predicate Information (identified by operation id):  
  60. ---------------------------------------------------  
  61.   
  62.    1 - filter("N1"=2 AND "N2"=2)  
  63.   
  64.   
  65. 統計資訊  
  66. ----------------------------------------------------------  
  67.           1  recursive calls  
  68.           0  db block gets  
  69.         401  consistent gets  
  70.           0  physical reads  
  71.           0  redo size  
  72.        9059  bytes sent via SQL*Net to client  
  73.         712  bytes received via SQL*Net from client  
  74.          29  SQL*Net roundtrips to/from client  
  75.           0  sorts (memory)  
  76.           0  sorts (disk)  
  77.         420  rows processed  

根據以上執行計劃,CBO估算將會返回16行資料,實際確實返回了420行,如果這個表要與多表關聯,基數一旦算錯,必然導致整個SQL的執行計劃全部出錯,從而導致SQL效能下降。

接下來再次在相關列上執行查詢,不過sql語句中包含了dynamic_sampling提示。

[sql] view plain copy
 print?
  1. SQL> select /*+ dynamic_sampling(t1 1) */  
  2.   2   small_vc  
  3.   3    from t1  
  4.   4   where n1 = 2  
  5.   5     and n2 = 2;  
  6.   
  7. 已選擇420行。  
  8.   
  9.   
  10. 執行計劃  
  11. ----------------------------------------------------------  
  12. Plan hash value: 3617692013  
  13.   
  14. --------------------------------------------------------------------------  
  15. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  16. --------------------------------------------------------------------------  
  17. |   0 | SELECT STATEMENT  |      |   370 |  6290 |    66   (0)| 00:00:01 |  
  18. |*  1 |  TABLE ACCESS FULL| T1   |   370 |  6290 |    66   (0)| 00:00:01 |  
  19. --------------------------------------------------------------------------  
  20.   
  21. Predicate Information (identified by operation id):  
  22. ---------------------------------------------------  
  23.   
  24.    1 - filter("N1"=2 AND "N2"=2)  
  25.   
  26. Note  
  27. -----  
  28.    - dynamic sampling used for this statement (level=2)  
  29.   
  30.   
  31. 統計資訊  
  32. ----------------------------------------------------------  
  33.           0  recursive calls  
  34.           0  db block gets  
  35.         401  consistent gets  
  36.           0  physical reads  
  37.           0  redo size  
  38.        9059  bytes sent via SQL*Net to client  
  39.         712  bytes received via SQL*Net from client  
  40.          29  SQL*Net roundtrips to/from client  
  41.           0  sorts (memory)  
  42.           0  sorts (disk)  
  43.         420  rows processed  




 

沒有直方圖的執行計劃預估結果集行數計算公式


公式摘抄於《催華-基於ORACLE的SQL優化》

轉載請註明連結地址:http://blog.csdn.net/launch_225/article/details/25432661


[html] view plain copy
 print?
  1. SQL> select * from t1;  
  2.   
  3.         N1 V1  
  4. ---------- --------  
  5.          1 a  
  6.          2 b  
  7.          2 c  
  8.          4 c  
  9.          5 d  
  10.          6 e  
  11.          6 f  
  12.          6 f  
  13.          9 g  
  14.         10 h  
  15.         11 i  
  16.   
  17.         N1 V1  
  18. ---------- --------  
  19.         12 i  
  20.         12 i  
  21.         14 j  
  22.         15 k  
  23.         16 l  
  24.         16 m  
  25.         18 n  
  26.   
  27. 18 rows selected.  

create index idx_t1_n1 on t1(n1);



[html] view plain copy
 print?
  1. 1. ====>沒有直方圖的預估結果集行數  
  2. SQL> select count(1) from t1;  
  3.   
  4.   COUNT(1)  
  5. ----------  
  6.         18  
  7.           
  8. SQL> select count(distinct n1) from t1;  
  9.   
  10. COUNT(DISTINCTN1)  
  11. -----------------  
  12.                13  
  13.                  
  14. SQL> select min(n1),max(n1) from t1;  
  15.   
  16.    MIN(N1)    MAX(N1)  
  17. ---------- ----------  
  18.          1         18  
  19.            
  20.     TABLE_NAME  COLUMN_NAME DENSITY NUM_BUCKETS HISTOGRAM  
  21. 1   T1  N1  0.0769230769230769  1   NONE  
  22. 2   T1  V1  0.0714285714285714  1   NONE  
  23.   
  24. 1.1=>範圍查詢  
  25. ------------------------------------------------------------------------------  
  26. (1)目標列大於指定的val,且位於low_value and high_value之間  
  27. selectivity=((HIGH_VALUE-VAL)/(HIGH_VALUE-LOW_VALUE))* NULL_ADJUST  
  28. NULL_ADJUST=(NUM_ROWS-NUM_NULLS)/NUM_ROWS  
  29.   
  30. SQL> select ((18-5)/(18-1))*1*18 from dual;  
  31.   
  32. ((18-5)/(18-1))*1*18  
  33. --------------------  
  34.           13.7647059                     
  35.   
  36.   1* select * from t1 where n1>5  
  37. SQL> /  
  38.   
  39. Execution Plan  
  40. ----------------------------------------------------------  
  41. Plan hash value: 1577308413  
  42.   
  43. -----------------------------------------------------------------------------------------  
  44. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  45. -----------------------------------------------------------------------------------------  
  46. |   0 | SELECT STATEMENT            |           |    14 |    70 |     2   (0)| 00:00:01 |  
  47. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |    14 |    70 |     2   (0)| 00:00:01 |  
  48. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |    14 |       |     1   (0)| 00:00:01 |  
  49. -----------------------------------------------------------------------------------------  
  50.   
  51. Predicate Information (identified by operation id):  
  52. ---------------------------------------------------  
  53.   
  54.    2 - access("N1">5)  
  55.      
  56. (2)目標列小於指定的val,且位於low_value and high_value之間  
  57. selectivity=((VAL-LOW_VALUE)/(HIGH_VALUE-LOW_VALUE))* NULL_ADJUST  
  58. NULL_ADJUST=(NUM_ROWS-NUM_NULLS)/NUM_ROWS  
  59.   
  60. SQL> select ((5-1)/(18-1))*1*18 from dual;  
  61.   
  62. ((5-1)/(18-1))*1*18  
  63. -------------------  
  64.          4.23529412  
  65.            
  66. SQL> select * from t1 where n1<5    
  67.   2  /  
  68.   
  69. Execution Plan  
  70. ----------------------------------------------------------  
  71. Plan hash value: 1577308413  
  72.   
  73. -----------------------------------------------------------------------------------------  
  74. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  75. -----------------------------------------------------------------------------------------  
  76. |   0 | SELECT STATEMENT            |           |     4 |    20 |     2   (0)| 00:00:01 |  
  77. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     4 |    20 |     2   (0)| 00:00:01 |  
  78. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     4 |       |     1   (0)| 00:00:01 |  
  79. -----------------------------------------------------------------------------------------  
  80.   
  81. Predicate Information (identified by operation id):  
  82. ---------------------------------------------------  
  83.   
  84.    2 - access("N1"<5)  
  85.      
  86. (3)目標列>=指定的val,且位於low_value and high_value之間  
  87. selectivity=((HIGH_VALUE-VAL)/(HIGH_VALUE-LOW_VALUE)+1/NUM_DISTINCT)* NULL_ADJUST  
  88. NULL_ADJUST=(NUM_ROWS-NUM_NULLS)/NUM_ROWS  
  89.   
  90. SQL> select ((18-5)/(18-1)+1/13)*1*18 from dual;  
  91.   
  92. ((18-5)/(18-1)+1/13)*1*18  
  93. -------------------------  
  94.                15.1493213  
  95.                  
  96. SQL> select * from t1 where n1>=5;      
  97.   
  98. Execution Plan  
  99. ----------------------------------------------------------  
  100. Plan hash value: 1577308413  
  101.   
  102. -----------------------------------------------------------------------------------------  
  103. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  104. -----------------------------------------------------------------------------------------  
  105. |   0 | SELECT STATEMENT            |           |    15 |    75 |     2   (0)| 00:00:01 |  
  106. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |    15 |    75 |     2   (0)| 00:00:01 |  
  107. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |    15 |       |     1   (0)| 00:00:01 |  
  108. -----------------------------------------------------------------------------------------  
  109.   
  110. Predicate Information (identified by operation id):  
  111. ---------------------------------------------------  
  112.   
  113.    2 - access("N1">=5)    
  114.      
  115.      
  116. (4)目標列<=指定的val,且位於low_value and high_value之間  
  117. selectivity=((VAL-LOW_VALUE)/(HIGH_VALUE-LOW_VALUE)+1/NUM_DISTINCT)* NULL_ADJUST  
  118. NULL_ADJUST=(NUM_ROWS-NUM_NULLS)/NUM_ROWS  
  119.   
  120. SQL> select ((5-1)/(18-1)+1/13)*1*18 from dual;  
  121.   
  122. ((5-1)/(18-1)+1/13)*1*18  
  123. ------------------------  
  124.                5.6199095  
  125.   
  126. SQL> select * from t1 where n1<=5;   
  127.   
  128. Execution Plan  
  129. ----------------------------------------------------------  
  130. Plan hash value: 1577308413  
  131.   
  132. -----------------------------------------------------------------------------------------  
  133. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  134. -----------------------------------------------------------------------------------------  
  135. |   0 | SELECT STATEMENT            |           |     6 |    30 |     2   (0)| 00:00:01 |  
  136. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     6 |    30 |     2   (0)| 00:00:01 |  
  137. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     6 |       |     1   (0)| 00:00:01 |  
  138. -----------------------------------------------------------------------------------------  
  139.   
  140. Predicate Information (identified by operation id):  
  141. ---------------------------------------------------  
  142.   
  143.    2 - access("N1"<=5)  
  144.      
  145. (5)列位於指定值VAL1和VAL2之間,且VAL1和VAL2在LOW_VALUE和HIGH_VALUE範圍  
  146. selectivity=((val2-val1)/(HIGH_VALUE-LOW_VALUE)+2/NUM_DISTINCT)* NULL_ADJUST  
  147. NULL_ADJUST=(NUM_ROWS-NUM_NULLS)/NUM_ROWS  
  148.   
  149. SQL> select ((10-5)/(18-1)+2/13)*1*18 from dual;  
  150.   
  151. ((10-5)/(18-1)+2/13)*1*18  
  152. -------------------------  
  153.                8.06334842  
  154.                  
  155. SQL> select * from t1 where n1 between 5 and 10;   
  156.   
  157. Execution Plan  
  158. ----------------------------------------------------------  
  159. Plan hash value: 1577308413  
  160.   
  161. -----------------------------------------------------------------------------------------  
  162. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  163. -----------------------------------------------------------------------------------------  
  164. |   0 | SELECT STATEMENT            |           |     8 |    40 |     2   (0)| 00:00:01 |  
  165. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     8 |    40 |     2   (0)| 00:00:01 |  
  166. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     8 |       |     1   (0)| 00:00:01 |  
  167. -----------------------------------------------------------------------------------------  
  168.   
  169. Predicate Information (identified by operation id):  
  170. ---------------------------------------------------  
  171.   
  172.    2 - access("N1">=5 AND "N1"<=10)  
  173. ------------------------------------------------------------------------------  
  174. 1.2 =》等值查詢  
  175. (1) 等值查詢且列上沒有NULL值沒有直方圖的計算公式  
  176. selectivity_without_null=(1/num_distinct)  
  177.   
  178. SQL> select 1/13*18 from dual;  
  179.   
  180.    1/13*18  
  181. ----------  
  182. 1.38461538  
  183.   
  184.   
  185. SQL> select * from t1 where n1=8;  
  186.   
  187. Execution Plan  
  188. ----------------------------------------------------------  
  189. Plan hash value: 1577308413  
  190.   
  191. -----------------------------------------------------------------------------------------  
  192. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  193. -----------------------------------------------------------------------------------------  
  194. |   0 | SELECT STATEMENT            |           |     1 |     5 |     2   (0)| 00:00:01 |  
  195. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     1 |     5 |     2   (0)| 00:00:01 |  
  196. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     1 |       |     1   (0)| 00:00:01 |  
  197. -----------------------------------------------------------------------------------------  
  198.   
  199. Predicate Information (identified by operation id):  
  200. ---------------------------------------------------  
  201.   
  202.    2 - access("N1"=8)  
  203.      
  204. (2)目標列沒有直方圖且有NULL值(通用於沒有直方圖時列的選擇率的計算)  
  205. selectivity_with_null=(1/num_distinct) * ((num_rows-num_nulls)/num_rows)     
  206.   
  207. SQL> select 1/13*((18-0)/18)*18 from dual;  
  208.   
  209. 1/13*((18-0)/18)*18  
  210. -------------------  
  211.          1.38461538  
  212.         
  213. --不存在空值也通用     
  214. SQL> select * from t1 where n1=8;  
  215.   
  216. Execution Plan  
  217. ----------------------------------------------------------  
  218. Plan hash value: 1577308413  
  219.   
  220. -----------------------------------------------------------------------------------------  
  221. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  222. -----------------------------------------------------------------------------------------  
  223. |   0 | SELECT STATEMENT            |           |     1 |     5 |     2   (0)| 00:00:01 |  
  224. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     1 |     5 |     2   (0)| 00:00:01 |  
  225. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     1 |       |     1   (0)| 00:00:01 |  
  226. -----------------------------------------------------------------------------------------  
  227.   
  228. Predicate Information (identified by operation id):  
  229. ---------------------------------------------------  
  230.   
  231.    2 - access("N1"=8)  
  232.      
  233.      

等頻直方圖預估結果集行計算公式

公式:摘自《催華-基於ORACLE的SQL優化》

連結地址:http://blog.csdn.net/launch_225/article/details/25472129

等頻:唯一值《=桶數


[html] view plain copy
 print?
  1. =>等頻(頻率)直方圖   
  2. -->density計算  
  3. density=1/(2*num_rows*null_adjust)  
  4. null_adjust=(num_rows-num_nulls)/num_rows  
  5. -->等值查詢,val在low_value and high_value之間,值等於某個endpoint_value  
  6. cardinality=num_rows * selectivity  
  7. selectivity=bucketsize/num_rows  
  8. bucketsize=current_endpoint_number-previous_endpoint_number  
  9.   
  10.  TABLE_NAME COLUMN_NAME NUM_BUCKETS LOW_VALUE   HIGH_VALUE  HISTOGRAM  
  11. 1   T1  N1  13  C102    C113    FREQUENCY  
  12.   
  13.   
  14.   
  15. -->density計算  
  16. density=1/(2*num_rows*null_adjust)  
  17.   
  18. SQL> select 1/(2*18*1) from dual;  
  19.   
  20. 1/(2*18*1)  
  21. ----------  
  22. .027777778  
  23.   
  24.     OWNER   TABLE_NAME  NUM_DISTINCT    DENSITY COLUMN_NAME  
  25. 1   AIKI    T1  13  0.0277777777777778  N1  
  26.   
  27.   
  28. -->等值查詢,val在low_value and high_value之間,值等於某個endpoint_value  
  29. cardinality=num_rows * selectivity  
  30. selectivity=bucketsize/num_rows  
  31. bucketsize=current_endpoint_number-previous_endpoint_number  
  32.   
  33.   1*  select * from t1 where n1=6  
  34. SQL> /  
  35.   
  36.   
  37. Execution Plan  
  38. ----------------------------------------------------------  
  39. Plan hash value: 1577308413  
  40.   
  41. -----------------------------------------------------------------------------------------  
  42. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  43. -----------------------------------------------------------------------------------------  
  44. |   0 | SELECT STATEMENT            |           |     3 |    15 |     2   (0)| 00:00:01 |  
  45. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     3 |    15 |     2   (0)| 00:00:01 |  
  46. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     3 |       |     1   (0)| 00:00:01 |  
  47. -----------------------------------------------------------------------------------------  
  48.   
  49. Predicate Information (identified by operation id):  
  50. ---------------------------------------------------  
  51.   
  52.    2 - access("N1"=6)  
  53.   
  54.             
  55. OWNER   TABLE_NAME  COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE  
  56. AIKI    T1  N1  1   1  
  57. AIKI    T1  N1  3   2  
  58. AIKI    T1  N1  4   4  
  59. AIKI    T1  N1  5   5       --previous_endpoint_number  
  60. AIKI    T1  N1  8   6       --current_endpoint_number  
  61. AIKI    T1  N1  9   9  
  62. AIKI    T1  N1  10  10  
  63. AIKI    T1  N1  11  11  
  64. AIKI    T1  N1  13  12  
  65. AIKI    T1  N1  14  14  
  66. AIKI    T1  N1  15  15  
  67. AIKI    T1  N1  17  16  
  68. AIKI    T1  N1  18  18  
  69.   
  70. bucketsize=8-5=3;  
  71. selectivity=3/18  
  72. cardinality=18*(3/18)=3;  

等高直方圖預估結果集行計算公式


摘自《催華-基於ORACLE的SQL優化》

連結地址:http://blog.csdn.net/launch_225/article/details/25474427




[html] view plain copy
 print?
  1. SQL> desc t1  
  2.  Name                                      Null?    Type  
  3.  ----------------------------------------- -------- ----------------------------  
  4.  N1                                                 NUMBER  
  5.  V1                                                 VARCHAR2(8)  
  6.   
  7. SQL> select * from t1;  
  8.   
  9.         N1 V1  
  10. ---------- --------  
  11.          1 a  
  12.          2 b  
  13.          2 c  
  14.          4 c  
  15.          5 d  
  16.          6 e  
  17.          6 f  
  18.          6 f  
  19.          9 g  
  20.         10 h  
  21.         11 i  
  22.         12 i  
  23.         12 i  
  24.         14 j  
  25.         15 k  
  26.         16 l  
  27.         16 m  
  28.         18 n  
  29.   
  30. 18 rows selected.  
  31.   
  32.   
  33.  TABLE_NAME COLUMN_NAME NUM_BUCKETS LOW_VALUE   HIGH_VALUE  HISTOGRAM  
  34. 1   T1  N1  10  C102    C113    HEIGHT BALANCED  
  35.   
  36.  OWNER  TABLE_NAME  COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE  
  37. 1   AIKI    T1  N1  0   1  
  38. 2   AIKI    T1  N1  1   2  
  39. 3   AIKI    T1  N1  2   4  
  40. 4   AIKI    T1  N1  4   6  
  41. 5   AIKI    T1  N1  5   10  
  42. 6   AIKI    T1  N1  6   12  
  43. 7   AIKI    T1  N1  7   14  
  44. 8   AIKI    T1  N1  8   15  
  45. 9   AIKI    T1  N1  9   16  
  46. 10  AIKI    T1  N1  10  18  
  47.   
  48.   
  49.   
  50. 1.查詢值為popular value:  
  51. cardinality=num_rows * SELECTIVITY  
  52. SELECTIVITY=(BUCKETS_THIS_POPULAR_VALUE/BUCKETS_TOTAL)* NULL_ADJUST  
  53. NULL_ADJUST=(NUM_ROWS-NUM_NULLS)/NUM_ROWS  
  54. BUCKETS_THIS_POPULAR_VALUE:POPULAR VALUE所點的bucket的數量,buckets_total:bucket的總數  
  55.   
  56. select * from t1 where n1=6;  
  57.   
  58. SELECTIVITY=(2/10)*1  
  59. SQL> select (2/10)*1*18 from dual;  
  60.   
  61. (2/10)*1*18  
  62. -----------  
  63.         3.6  
  64.           
  65. SQL> select * from t1 where n1=6;  
  66.   
  67.   
  68. Execution Plan  
  69. ----------------------------------------------------------  
  70. Plan hash value: 1577308413  
  71.   
  72. -----------------------------------------------------------------------------------------  
  73. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  74. -----------------------------------------------------------------------------------------  
  75. |   0 | SELECT STATEMENT            |           |     4 |    20 |     2   (0)| 00:00:01 |  
  76. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     4 |    20 |     2   (0)| 00:00:01 |  
  77. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     4 |       |     1   (0)| 00:00:01 |  
  78. -----------------------------------------------------------------------------------------  
  79.   
  80. Predicate Information (identified by operation id):  
  81. ---------------------------------------------------  
  82.   
  83.    2 - access("N1"=6)  
  84.      
  85.   
  86. --2.非popular value  
  87. --(>=10.2.0.4版本)  
  88. selectivity=newdensity * null_adjust  
  89. null_adjust=(num_rows-num_nulls)/num_rows  
  90. newdensity=(buckets_total-buckets_all_popular_values)/buckets_total/(ndv-popular_value.count)  
  91. olddensity=sum(np.count(i) * np.count(i))/((num_rows-num_nulls)* sum(np.count(i)))  
  92. ndv=num_distinct  
  93. olddensity儲存在dba_***_col_statistics的density中  
  94. buckets_all_popular_values:所有的pupular value所佔的bucket的數量,buckets_total:bucket的總數  
  95. popular_values.count:pupular value的個數,NP.count(i):每個nopopular value在目標表中的記錄數  
  96.   
  97.              
  98. newdensity=(10-(4-2))/10/(13-1)  
  99. selectivity=(10-(4-2))/10/(13-1)  
  100. cardinality=(10-(4-2))/10/(13-1)*18  
  101.   
  102. SQL> select  (10-(4-2))/10/(13-1)*18 from dual;  
  103.   
  104. (10-(4-2))/10/(13-1)*18  
  105. -----------------------  
  106.                     1.2  
  107.                       
  108. SQL> select * from t1 where n1=12;  --12為非popular value  
  109. Execution Plan  
  110. ----------------------------------------------------------  
  111. Plan hash value: 1577308413  
  112.   
  113. -----------------------------------------------------------------------------------------  
  114. | Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  115. -----------------------------------------------------------------------------------------  
  116. |   0 | SELECT STATEMENT            |           |     1 |     5 |     2   (0)| 00:00:01 |  
  117. |   1 |  TABLE ACCESS BY INDEX ROWID| T1        |     1 |     5 |     2   (0)| 00:00:01 |  
  118. |*  2 |   INDEX RANGE SCAN          | IDX_T1_N1 |     1 |       |     1   (0)| 00:00:01 |  
  119. -----------------------------------------------------------------------------------------  
  120.   
  121. Predicate Information (identified by operation id):  
  122. ---------------------------------------------------  
  123.   
  124.    2 - access("N1"=12)  
  125.   
  126.                   
  127. --2.1.版本10.2.0.4(不含10.2.0.4,10.2.0.1)以上版本  
  128. selectivity=olddensity * null_adjust  
  129. null_adjust=(num_rows-num_nulls)/num_rows  
  130. olddensity=sum(np.count(i)* np.count(i))/((num_rows-num_nulls)*sum(np.count(i)))  
  131. olddensity儲存在dba_***_col_statistics的density中  
  132. NP.count(i):每個nopopular value在目標表中的記錄數  
  133.   
  134. --2.1版本10.2.0.1  
  135. selectivity=olddensity * null_adjust  
  136. null_adjust=(num_rows-num_nulls)/num_rows  
  137. olddensity=sum(dv.count(i) * dv.count(i))/((num_rows-num_nulls)* sum(dv.count(i)))  
  138.   
  139. olddensity用於實際計算可選擇率和結果集的Cardinality  
  140. olddensity也會儲存在資料字典DBA_TAB_COL_STATISTICS,DBA_PART_COL_STATISTICS和DBA_SUBPART_COL_STATISTICS中的DENSITY中  
  141. DV.count(i)表示的是目標列的每個DISTINCT值在目標表中的記錄數  







About Me

...............................................................................................................................

● 本文作者:小麥苗,只專注於資料庫的技術,更注重技術的運用

● 本文在itpub(http://blog.itpub.net/26736162)、部落格園(http://www.cnblogs.com/lhrbest)和個人微信公眾號(xiaomaimiaolhr)上有同步更新

● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/

● 本文部落格園地址:http://www.cnblogs.com/lhrbest

● 本文pdf版及小麥苗雲盤地址:http://blog.itpub.net/26736162/viewspace-1624453/

● 資料庫筆試面試題庫及解答:http://blog.itpub.net/26736162/viewspace-2134706/

● QQ群:230161599     微信群:私聊

● 聯絡我請加QQ好友(646634621),註明新增緣由

● 於 2017-06-02 09:00 ~ 2017-06-30 22:00 在魔都完成

● 文章內容來源於小麥苗的學習筆記,部分整理自網路,若有侵權或不當之處還請諒解

● 版權所有,歡迎分享本文,轉載請保留出處

...............................................................................................................................

拿起手機使用微信客戶端掃描下邊的左邊圖片來關注小麥苗的微信公眾號:xiaomaimiaolhr,掃描右邊的二維碼加入小麥苗的QQ群,學習最實用的資料庫技術。

CBO中基數(cardinality)、可選擇率(selectivity)的計算公式
DBA筆試面試講解
歡迎與我聯絡

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2140358/,如需轉載,請註明出處,否則將追究法律責任。

相關文章