Chinaunix首页 | 论坛 | 博客
  • 博客访问: 152567
  • 博文数量: 39
  • 博客积分: 825
  • 博客等级: 准尉
  • 技术积分: 955
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-25 14:05
文章分类

全部博文(39)

文章存档

2014年(4)

2013年(13)

2012年(22)

我的朋友

分类: Oracle

2012-11-10 22:17:19

  1):等值选择率
 
【创建实验表】

DROP TABLE SS PURGE;
CREATE TABLE SS(ID) AS SELECT mod(LEVEL,80) FROM dual CONNECT BY LEVEL<=10000;
 
--收集统计信息
BEGIN
  dbms_stats.gather_table_stats(ownname          => USER,
                                tabname          => 'ss',
                                estimate_percent => 100,
                                method_opt       => 'for all columns size 1');
END;
/

--查看统计信息
SELECT UTC.num_distinct,
       utl_raw.cast_to_number(utc.low_value) AS lval,
       utl_raw.cast_to_number(UTC.high_value) AS hval,
       utc.density,
       utc.num_nulls,
       utc.HISTOGRAM
FROM   user_tab_col_statistics UTC
WHERE  utc.table_name = 'SS';
--80 0 79 0.0125 0
 
--评估基数
DELETE FROM plan_table;
EXPLAIN PLAN FOR SELECT ID FROM SS WHERE ss.Id=1;
SELECT CARDINALITY FROM plan_table WHERE ID=0;
--125 = num_rows * (1/80)
 
--空值引入
UPDATE SS SET ID=NULL WHERE ID BETWEEN 20 AND 30;
COMMIT;
 
--重新收集统计信息
 
--查看统计信息
69 0 79 0.0144927536231884 1375 NONE
 
--评估基数
125
 
SELECT 10000 * (1 / 69 * ((10000 - 1375) / 10000)) FROM dual;
--125
 
  【结论】
 
  等值选择率的计算公式为:
( 1 / num_distinct ) * ( (num_rows - num_nulls) / num_rows )。
 
  下面我们研究下类似等值操作的in和not in操作的选择率计算公式。
 
______________________________________________________________________
 
  2):IN-list选择率
 
【普通基数评估】
 
DELETE FROM plan_table;
EXPLAIN PLAN FOR SELECT ID FROM SS WHERE ss.Id in(1,2,3);
SELECT CARDINALITY FROM plan_table WHERE ID=0;
--375
 
SELECT 10000 * (1 / 69 * ((10000 - 1375) / 10000)) *3 FROM dual;
 
  其实in操作的选择率计算公式还是以等值选择率计算公式为基础,即:
 
( 1 / num_distinct ) * ( (num_rows - num_nulls) / num_rows ) * IN_list_cnt。
 
  但是这里值得注意一点的就是,如果in中包含null,则cbo会将null和普通常量一样
对待,这种做法肯定是错误的。

【NOT IN选择率】
 
DELETE FROM plan_table;
EXPLAIN PLAN FOR SELECT ID FROM SS WHERE ss.Id NOT in(1,2,3);
SELECT CARDINALITY FROM plan_table WHERE ID=0;
6141
 
 
oracle处理not in时是改写为如下等效谓词:
 
WHERE ss.id<>1 AND ss.id<>2 AND ss.id<>3
 
而我们知道对于and关系操作的选择率为:S1*S2*S3...
其中S代表选择率,而1,2,3...分别代表各谓词条件。
 
所以我们只要知道<>操作的选择率计算方法就可以推算
出not IN操作的选择率计算方式:
 
DELETE FROM plan_table;
EXPLAIN PLAN FOR SELECT ID FROM SS WHERE ss.Id<>1;
SELECT CARDINALITY FROM plan_table WHERE ID=0;
8500
 
SELECT (1 - ((1 / 69) * ((10000 - 1375) / 10000)) - 1375 / 10000) * 10000
FROM   dual;
--8500
 
BEGIN
  dbms_stats.set_table_stats(ownname => USER,
                             tabname => 'ss',
                             numrows => 22556688);
  dbms_stats.set_column_stats(ownname => USER,
                              tabname => 'ss',
                              colname => 'id',
                              nullcnt => 589977);
END;
/
DELETE FROM plan_table;
EXPLAIN PLAN FOR SELECT ID FROM SS WHERE ss.Id<>1;
SELECT CARDINALITY FROM plan_table WHERE ID=0;
--21648353
 
SELECT (1 - ((1 / 69) * ((22556688 - 589977) / 22556688)) -
       589977 / 22556688)*22556688
FROM   dual;
--21648352.8695652
 
所以我们得出<>操作的选择率计算公式为:
(1 - ((1 / num_distinct) * ((num_rows - num_nulls) / num_rows)) - num_nulls / num_rows),
字面解释就是1减去普通等值选择率,再减去空值选择率(in操作就没not IN明智)。
 
而得到NOT IN操作的选择率公式即为:
POWER((1 - ((1 / num_distinct) * ((num_rows - num_nulls) / num_rows)) - num_nulls / num_rows) , in_list_cnt)。
阅读(577) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~