查看: 36950|回复: 67

[精华] 一次SQL Tuning引出来的not in , not exists 语句的N种写法

[复制链接]
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
跳转到指定楼层
1#
发表于 2005-8-10 12:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
今天,发现production上有一个SQL 耗了特多CPU time,于是抓了下来,看看是否可以tuning .

原始 SQL 如下(table名字改了一下):

[php]
DELETE FROM test_table_1 a
      WHERE NOT EXISTS (SELECT *
                          FROM test_table_2 b
                         WHERE a.parent_id = b.id)

执行时间 60S
Buffer get: 160W
执行计划

Operation        Object Name        Rows        Bytes        Cost        Object Node        In/Out        PStart        PStop

DELETE STATEMENT Optimizer Mode=CHOOSE                1                   2069                                                       
  DELETE        TEST_TABLE_1                                                                                 
    HASH JOIN ANTI                1          26          2069                                                       
      TABLE ACCESS FULL        TEST_TABLE_1        5 M        106 M        1210                                                       
      TABLE ACCESS FULL        TEST_TABLE_2        46 K        181 K        48                                                       
                                                     

其中
test_table_1 row count 为 4百万
test_table_2 为 为 4万


TEST_TABLE_1上的 parent_id上有index.
  
...
[/php]
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
2#
 楼主| 发表于 2005-8-10 13:23 | 只看该作者

第一种变换(not in)

现在来试一下变换

用not in 代替 not exists

[php]

DELETE FROM test_table_1 a
      WHERE parent_id  NOT IN (SELECT id
                          FROM test_table_2 b)

..
[/php]
在9i 里面,Oracle把这个语句和前面的语句看为同一个语句,当然执行计划和执行时间也没有差别。

使用道具 举报

回复
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
3#
 楼主| 发表于 2005-8-10 13:40 | 只看该作者

第二种变换(minus)

第二种变换(minus)

用minus,

把 not exists 部分先用minus去掉,再用in 来选择data。
[php]
DELETE FROM test_table_1 a
      WHERE parent_id IN (SELECT parent_id
                            FROM test_table_1
                          MINUS
                          SELECT ID
                            FROM test_table_2 b)

执行时间 : 8S
Buffer get :20000

执行计划

Operation        Object Name        Rows        Bytes        Cost        Object Node        In/Out        PStart        PStop

DELETE STATEMENT Optimizer Mode=CHOOSE                2 G                 23052                                                       
  DELETE        TEST_TABLE_1                                                                                 
    HASH JOIN                2 G        89G        23052                                                       
      VIEW        SYS.VW_NSO_1        5 M        63 M        5488                                                       
        MINUS                                                                                         
          SORT UNIQUE                5 M        19 M                                                              
            INDEX FAST FULL SCAN        TEST_INDX_1        5 M        19 M        457                                                       
          SORT UNIQUE                46 K        181 K                                                              
            TABLE ACCESS FULL        TEST_TABLE_2        46 K        181 K        48                                                       
      TABLE ACCESS FULL        TEST_TABLE_1        5 M        106 M        1210                                                       

速度提高了不少。
...
[/php]

使用道具 举报

回复
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
4#
 楼主| 发表于 2005-8-10 13:46 | 只看该作者

(minus + distinct )

在 minus之前加个 distinct看看
[php]
DELETE FROM test_table_1 a
      WHERE parent_id IN (SELECT distinct parent_id
                            FROM test_table_1
                          MINUS
                          SELECT ID
                            FROM test_table_2 b)

执行时间 : 8S
Buffer get :20000

执行计划

Operation    Object Name    Rows    Bytes    Cost    Object Node    In/Out    PStart    PStop

DELETE STATEMENT Optimizer Mode=CHOOSE        2 G         23052                                       
  DELETE    TEST_TABLE_1                                                     
    HASH JOIN        2 G    89G    23052                                       
      VIEW    SYS.VW_NSO_1    5 M    63 M    5488                                       
        MINUS                                                         
          SORT UNIQUE        5 M    19 M                                          
            INDEX FAST FULL SCAN    TEST_INDX_1    5 M    19 M    457                                       
          SORT UNIQUE        46 K    181 K                                          
            TABLE ACCESS FULL    TEST_TABLE_2    46 K    181 K    48                                       
      TABLE ACCESS FULL    TEST_TABLE_1    5 M    106 M    1210           

和上一个一模一样。
由于用了in 的操作符,Oracle内部就给加了个  (SORT UNIQUE        )                          
[/php]

使用道具 举报

回复
论坛徽章:
0
5#
发表于 2005-8-10 13:47 | 只看该作者
沙发

使用道具 举报

回复
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
6#
 楼主| 发表于 2005-8-10 14:08 | 只看该作者
可以用 in ,当然就可以用 exists 或者join 了(delete不好用join,只好改为SELECT)
[php]
Join

SELECT * FROM (SELECT DISTINCT parent_id
                                   FROM TEST_TABLE_1
                                 MINUS
                                 SELECT id
                                   FROM TEST_TABLE_2 b ) tmp,TEST_TABLE_1 a
      WHERE  tmp.parent_id   = a.id
执行时间 8S
Buffer get: 20000

Operation        OBJECT Name        ROWS        Bytes        Cost        OBJECT Node        IN/OUT        PStart        PStop

SELECT STATEMENT Optimizer MODE=CHOOSE                5 M                 4493                                                       
  HASH JOIN                5 M        237 M        4493                                                       
    VIEW                9 K        125 K        3161                                                       
      MINUS                                                                                         
        SORT UNIQUE                9 K        38 K                                                              
          INDEX FAST FULL SCAN        TEST_INDX_1        5 M        19 M        457                                                       
        SORT UNIQUE                46 K        181 K                                                              
          TABLE ACCESS FULL        TEST_TABLE_2        46 K        181 K        48                                                       
    TABLE ACCESS FULL        TEST_TABLE_1        5 M        164 M        1210                        

用exists的

DELETE FROM TEST_TABLE_1 a  WHERE EXISTS (SELECT 1 FROM (SELECT DISTINCT parent_id
                                   FROM TEST_TABLE_1
                                 MINUS
                                 SELECT id
                                   FROM TEST_TABLE_2 b )        
   WHERE parent_id  = a.parent_id
执行效率惨不忍睹

...
[/php]

使用道具 举报

回复
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
7#
 楼主| 发表于 2005-8-10 14:24 | 只看该作者
除了上面几种,还有一种不是很常用的方法
就是(outer join + is null )(也是为了方便,先用SELECT来演示,)

[php]
select * from test_table_1 a,test_table_2 b
where a.parent_id = b.id(+)
and b.id is null

在本例中执行效率也挺差的。(超过1min)
Operation        OBJECT Name        ROWS        Bytes        Cost        OBJECT Node        IN/OUT        PStart        PStop

SELECT STATEMENT Optimizer MODE=CHOOSE                24 M                 3311                                                       
  FILTER                                                                                         
    HASH JOIN OUTER                                                                                         
      TABLE ACCESS FULL        TEST_TABLE_1        5 M        164 M        1210                                                       
      TABLE ACCESS FULL        TEST_TABLE_2        46 K        5 M        48           

。。
[/php]

[php]

加个 distinct 看看

SELECT a.*   FROM  TEST_TABLE_2 b ,(SELECT DISTINCT parentid FROM TEST_TABLE_1) a
                                  WHERE a.parentid = b.id(+)
                                        AND b.id IS NULL
                                                       
执行时间:1S
Buffer gets : 1000       
                                                                               
Operation        OBJECT Name        ROWS        Bytes        Cost        OBJECT Node        IN/OUT        PStart        PStop

SELECT STATEMENT Optimizer MODE=CHOOSE                47 K                 3117                                                       
  FILTER                                                                                         
    HASH JOIN OUTER                                                                                         
      VIEW                9 K        125 K        3067                                                       
        SORT UNIQUE                9 K        38 K        3067                                                       
          INDEX FAST FULL SCAN        TEST_INDX_1        5 M        19 M        457                                                       
      TABLE ACCESS FULL        TEST_TABLE_2        46 K        181 K        48                  
          
...
[/php]

使用道具 举报

回复
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
8#
 楼主| 发表于 2005-8-10 14:33 | 只看该作者
看来最后一种是最有效哦
改成DELETE形式看看

[php]
DELETE FROM TEST_TABLE_1 WHERE parentid IN (
SELEC parentid   FROM  TEST_TABLE_2 b ,(SELECT DISTINCT parentid FROM TEST_TABLE_1) a
                                  WHERE a.parentid = b.id(+)
                                        AND b.id IS NULL
)


成功了,执行时间 1S (实际上是 800-900ms)
Buffer_gets :1000
执行计划
Operation        OBJECT Name        ROWS        Bytes        Cost        OBJECT Node        IN/OUT        PStart        PStop

DELETE STATEMENT Optimizer MODE=CHOOSE                6 K                 119                                                       
  DELETE        TEST_TABLE_1                                                                                 
    HASH JOIN SEMI                6 K        171 K        119                                                       
      TABLE ACCESS FULL        TEST_TABLE_1        6 K        91 K        26                                                       
      VIEW        SYS.VW_NSO_1        2 M        30 M        71                                                       
        FILTER                                                                                         
          HASH JOIN OUTER                                                                                         
            INDEX FAST FULL SCAN        TEST_INDX_1        6 K        18 K        23                                                       
            TABLE ACCESS FULL        TEST_TABLE_2        46 K        136 K        47         

...
[/php]

使用道具 举报

回复
论坛徽章:
131
2006年度最佳技术回答
日期:2007-01-24 12:58:48福特
日期:2013-10-24 13:57:422014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上加薪
日期:2014-02-19 11:55:142013年新春福章
日期:2013-02-25 14:51:24
9#
 楼主| 发表于 2005-8-10 14:34 | 只看该作者
可能not exists / not in 还有其他变换形式,大家可以讨论讨论。

使用道具 举报

回复
论坛徽章:
3
会员2006贡献徽章
日期:2006-04-17 13:46:34授权会员
日期:2006-07-17 09:47:16
10#
发表于 2005-8-10 14:35 | 只看该作者

用HASH

这两个表一大一小,正好用HASH,速度可以提高到秒级,不信你就试试

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表