首页 - 数据库 - SQL Server

CBO与RBO下的IN/EXISTS

发布时间: 2007-04-19 05:04    作者: 未知    来源: 未知    浏览:    评论

    晚上抽空看了看ask tom的RSS,发现两篇应该说很入门的关于IN/EXISTS的文章:http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:953229842074,http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:442029737684    文章很长,也没有仔细看完,不过有些东西还是很有意思的,动手实验了一下。大家随便看看咯,Tom当初回答问题的环境我已经没办法测试了,在10.1.0.4下做了些测试,CBO和RBO(使用hints)下还是很大区别的。SQL> select * from scott.emp;     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20      7499 ALLEN      SALESMAN        7698 20-2月 -81           1600        300         30      7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30      7566 JONES      MANAGER         7839 02-4月 -81           2975                    20      7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30      7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30      7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20      7839 KING       PRESIDENT            17-11月-81           5000                    10      7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30      7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20      7900 JAMES      CLERK           7698 03-12月-81            950                    30      7902 FORD       ANALYST         7566 03-12月-81           3000                    20      7934 MILLER     CLERK           7782 23-1月 -82           1300                    10已选择14行。执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=14 Bytes=1218)   1    0   TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=1218)SQL> create table tmp_emp as select * from scott.emp where ename like 'S%';表已创建。SQL> select * from tmp_emp;     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=2 Bytes=174)   1    0   TABLE ACCESS (FULL) OF 'TMP_EMP' (TABLE) (Cost=3 Card=2 Bytes=174)先测试一下IN:SQL> select * from tmp_emp where ename in (select ename from scott.emp);     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=7 Card=2 Bytes=188)   1    0   HASH JOIN (SEMI) (Cost=7 Card=2 Bytes=188)   2    1     TABLE ACCESS (FULL) OF 'TMP_EMP' (TABLE) (Cost=3 Card=2 Bytes=174)   3    1     TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=98)表没有做过分析,使用了HASH JOIN (SEMI)。Tom在回答问题的时候提到:Select * from T1 where x in ( select y from T2 )is typically processed as:select *   from t1, ( select distinct y from t2 ) t2 where t1.x = t2.y;The subquery is evaluated, distincted, indexed (or hashed or sorted) and then joined to the original table -- typically.显然第二个查询是很奇怪的。也正是这个促使我打开了数据库测试,难道一个IN还需要先DISTINCT一下?没见过IN产生排序操作啊。SQL> ed已写入 file afiedt.buf  1  select * from tmp_emp,(select distinct ename from scott.emp) t  2* where tmp_emp.ename=t.enameSQL> /     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO ENAME---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20 SMITH      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20 SCOTT执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=2 Bytes=188)   1    0   VIEW (Cost=8 Card=2 Bytes=188)   2    1     SORT (UNIQUE) (Cost=8 Card=2 Bytes=202)   3    2       HASH JOIN (Cost=7 Card=2 Bytes=202)   4    3         TABLE ACCESS (FULL) OF 'TMP_EMP' (TABLE) (Cost=3 Card=2 Bytes=188)   5    3         TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=98)显然不一样了。那么改用RULE模式:SQL> ed已写入 file afiedt.buf  1  select /*+ rule */ * from tmp_emp,(select distinct ename from scott.emp) t  2* where tmp_emp.ename=t.enameSQL> /     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO ENAME---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- ----------      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20 SCOTT      7369 SMITH      CLERK           7902 17-12月-80            800                    20 SMITH执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=HINT: RULE   1    0   MERGE JOIN   2    1     VIEW   3    2       SORT (UNIQUE)   4    3         TABLE ACCESS (FULL) OF 'EMP' (TABLE)   5    1     SORT (JOIN)   6    5       TABLE ACCESS (FULL) OF 'TMP_EMP' (TABLE)再试试加上索引:SQL> alter table tmp_emp add constraints pk_tmpemp primary key(ename);表已更改。分析一下:SQL> analyze table tmp_emp compute statistics for table for all indexed columns;表已分析。SQL> analyze table scott.emp compute statistics for table for all columns;表已分析。SQL> select *  2  from tmp_emp e,(select distinct ename from scott.emp) t  3  where e.ename=t.ename;     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO ENAME---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20 SMITH      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20 SCOTT执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=6 Card=2 Bytes=188)   1    0   VIEW (Cost=6 Card=2 Bytes=188)   2    1     SORT (UNIQUE) (Cost=6 Card=2 Bytes=88)   3    2       NESTED LOOPS (Cost=5 Card=2 Bytes=88)   4    3         TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=70)   5    3         TABLE ACCESS (BY INDEX ROWID) OF 'TMP_EMP' (TABLE) (Cost=1 Card=1 Bytes=39)   6    5           INDEX (UNIQUE SCAN) OF 'PK_TMPEMP' (INDEX (UNIQUE)) (Cost=0 Card=1)SQL> ed已写入 file afiedt.buf  1  select /*+rule*/ *  2  from tmp_emp e,(select distinct ename from scott.emp) t  3* where e.ename=t.enameSQL> /     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO ENAME---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- ----------      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20 SCOTT      7369 SMITH      CLERK           7902 17-12月-80            800                    20 SMITH执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=HINT: RULE   1    0   NESTED LOOPS   2    1     VIEW   3    2       SORT (UNIQUE)   4    3         TABLE ACCESS (FULL) OF 'EMP' (TABLE)   5    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_EMP' (TABLE)   6    5       INDEX (UNIQUE SCAN) OF 'PK_TMPEMP' (INDEX (UNIQUE))SQL> select * from tmp_emp where ename in (select ename from scott.emp);     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20      7369 SMITH      CLERK           7902 17-12月-80            800                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=5 Card=1 Bytes=44)   1    0   NESTED LOOPS (Cost=5 Card=1 Bytes=44)   2    1     SORT (UNIQUE) (Cost=3 Card=14 Bytes=70)   3    2       TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=70)   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_EMP' (TABLE) (Cost=1 Card=1 Bytes=39)   5    4       INDEX (UNIQUE SCAN) OF 'PK_TMPEMP' (INDEX (UNIQUE)) (Cost=0 Card=1)SQL> select /*+rule*/ * from tmp_emp where ename in (select ename from scott.emp);     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20      7369 SMITH      CLERK           7902 17-12月-80            800                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=HINT: RULE   1    0   NESTED LOOPS   2    1     VIEW OF 'VW_NSO_1' (VIEW)   3    2       SORT (UNIQUE)   4    3         TABLE ACCESS (FULL) OF 'EMP' (TABLE)   5    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_EMP' (TABLE)   6    5       INDEX (UNIQUE SCAN) OF 'PK_TMPEMP' (INDEX (UNIQUE))这次确实接近了很多,总体上看和Tom说的情况差不多。不过RBO这种模式对后边的表对应列选择性低时应该很好,而其他情况恐怕不见得是优化的。下面看看EXISTS:SQL> select * from tmp_emp t where exists(select null from scott.emp e where t.ename=e.ename);     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20      7369 SMITH      CLERK           7902 17-12月-80            800                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=5 Card=1 Bytes=44)   1    0   NESTED LOOPS (Cost=5 Card=1 Bytes=44)   2    1     SORT (UNIQUE) (Cost=3 Card=14 Bytes=70)   3    2       TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=70)   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_EMP' (TABLE) (Cost=1 Card=1 Bytes=39)   5    4       INDEX (UNIQUE SCAN) OF 'PK_TMPEMP' (INDEX (UNIQUE)) (Cost=0 Card=1)看着和用IN一样哦。SQL> select /*+rule*/ * from tmp_emp t where exists(select null from scott.emp e where t.ename=e.ename);     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=HINT: RULE   1    0   FILTER   2    1     TABLE ACCESS (FULL) OF 'TMP_EMP' (TABLE)   3    1     TABLE ACCESS (FULL) OF 'EMP' (TABLE)用RBO就不同咯!SQL> select t.* from tmp_emp t,scott.emp e where t.ename=e.ename;     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO---------- ---------- --------- ---------- -------------- ---------- ---------- ----------      7369 SMITH      CLERK           7902 17-12月-80            800                    20      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20执行计划----------------------------------------------------------   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=5 Card=2 Bytes=88)   1    0   NESTED LOOPS (Cost=5 Card=2 Bytes=88)   2    1     TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=70)   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'TMP_EMP' (TABLE) (Cost=1 Card=1 Bytes=39)   4    3       INDEX (UNIQUE SCAN) OF 'PK_TMPEMP' (INDEX (UNIQUE)) (Cost=0 Card=1)这样看来在CBO下,使用内关联、IN、EXISTS很可能得到同一个执行计划(更多的情况就不测试了),优化器会发现三者的语义是相同的;而在较早的RBO下EXISTS采用FILTER而IN相当于对子查询先DISTINCT后关联,内关联则是直接关联就行了。Tom在他最新的回复中这样说:Use the RBO and see what you see.  way back when I wrote this, that was the "more popular" of the two perhapstoday in 2005, what I said years ago using the RBO does not apply to the CBO. the cbo is smart enough to recognize these two things are effectively the same.IN相当于对子查询先DISTINCT后关联这一条真的没有想通,RBO为什么做这样的事呢?实在是没有普适性,我个人的理解就是设计的时候认为IN后面是跟一个值列表的情况居多,当然先把值算出来,然后NL就可以了,如果后面是一个表里的值那也就一样处理;而且使用IN的时候也许大多是子查询对外层查询的筛选性高,即外层的表较大,而子查询的返回值较少。看看不同情况的不同执行计划,Oracle在CBO上确实还是花了点心思的,赫赫。结论:在RBO下,使用IN还是EXISTS需要视情况而定,只要记住使用IN存在排序和DISTINCT这一步骤应该就不难判断;CBO下优化器会为你选择,怎么写就只是习惯问题了。

TAG

Smile Big Smile Surprise Stick out tongue Wink Sad Tongue Tied Indifferent Crying Embarrassed Cool Angry Angel Devil [8-|] [:#] [:-*] [:^)] [<:o)] [|-)] Yes Beer Left Hug Music Star Time Snail Pizza Automobile Umbrella Computer Storm [mo] [8o|] [^o)] [+o(] [*-)] [8-)] Coffee No Drinks [Z] Right Hug Cake Broken Heart Gift Wilted Flower Movie Dog Idea Sleep Email Travel Paradise
呢称:

加粗 斜体 下划线 链接 图片 代码 邮件地址 引用 列表

最多只能输入100个字符

Tags

SQL 数据库 asp.net C# XML 控件 .NET教程 程序 事件 数据 安全 代码 Server 客户端 验证 数据库专栏 接口 文件 Oracle DataSet 函数 DataGrid 问题 .net return C#语言 JavaScript 服务 IIS 对象 语句 windows 继承 时间 web.config 设计 开发 参数 变量 解决 字符 ADO.net 环境 VB.Net语言 web 异常 工具 服务器 计算 实例 OLEDB Application VB Word WebService insert asp net 安装 记录

精华推荐

更多

精品下载

更多