oracle单表选择率(selectivity)——计算执行计划的基数

简介:

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。


本文转自 张冲andy 博客园博客,原文链接:http://www.cnblogs.com/andy6/p/5765187.html   ,如需转载请自行联系原作者

相关文章
|
5月前
|
SQL Oracle 关系型数据库
Oracle查询优化-计算字符在字符串中出现的次数
【2月更文挑战第3天】【2月更文挑战第7篇】只接上SQL
133 0
|
5月前
|
SQL Oracle 关系型数据库
Oracle之日期计算相关函数
Oracle之日期计算相关函数
81 0
|
14天前
|
SQL Oracle 关系型数据库
Oracle SQL:了解执行计划和性能调优
Oracle SQL:了解执行计划和性能调优
27 1
|
5月前
|
监控 Oracle 关系型数据库
Oracle 12c的Adaptive执行计划:数据的“聪明导航员”
【4月更文挑战第19天】Oracle 12c的Adaptive执行计划是数据库查询的智能优化工具,能根据实际运行情况动态调整执行策略。它像一个聪明的导航系统,不仅生成初始执行计划,还能实时监控并适应统计信息和资源变化,例如自动切换索引或调整并行度。此外,它支持自适应连接和统计信息收集,提升处理复杂查询和变化数据环境的能力。数据管理员应充分利用这一特性来优化查询性能和用户体验。
|
5月前
|
SQL Oracle 关系型数据库
Oracle之有哪些日期计算函数?
Oracle之有哪些日期计算函数?
143 0
|
SQL Oracle 关系型数据库
ORACLE——一条SQL计算同比、环比
ORACLE——一条SQL计算同比、环比
|
SQL Oracle 关系型数据库
Oracle中计算除法 ,解决除数为零报错问题
Oracle中计算除法 ,解决除数为零报错问题
329 0
|
监控 Oracle 关系型数据库
Oracle 计算时间差
Oracle 计算时间差
86 0
|
SQL 缓存 监控
Oracle中如何生成有用的SQL 执行计划(译)
漫画戴夫·艾伦(Dave Allen)曾经讲过一个古老的笑话,一个旅行者问路人去某个城镇的路,路人只是说:“如果我是你,我就不会从这里开始。”
157 0
|
SQL 存储 缓存
获取和解读Oracle中SQL的执行计划(译文)
生成和显示 SQL 语句的执行计划是大多数 DBA、SQL 开发人员和性能专家的常见任务,因为它提供了 SQL 语句执行性能相关的信息。执行计划显示执行 SQL 语句的详细步骤,这些步骤表示为一组使用和生成行的数据库运算符。运算符的顺序和实现由查询优化器根据查询转换和物理优化技术来决定。
305 0

推荐镜像

更多