SQL EXISTS为什么选择rownum导致低效的执行计划?

时间:2013-12-06 22:14:40

标签: sql oracle exists query-performance rownum

问题

我试图理解为什么在这两个Oracle语法更新查询中看起来微不足道的原因导致执行计划完全不同。

查询1:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select *    
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3)

查询2:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select rownum    
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3)

正如您所看到的,两者之间的唯一区别是查询2中的子查询返回rownum而不是每行的值。

这两者的执行计划不可能更加不同:

  • Query1 - 从两个表中提取总结果,并使用sort和hashjoin返回结果。这有利于2,346的成本(尽管使用了EXISTS条款和有凝聚力的子查询)。

  • Query2 - 同时拉取两个表结果,但使用计数和过滤器来完成相同的任务,并以惊人的77,789,696成本返回执行计划!我应该注意到他的查询只是挂在我身上所以我实际上并不是肯定的,这会返回相同的结果(虽然我相信它应该)。

根据我对Exists子句的理解,它只是一个简单的布尔检查,它运行在主表的每一行。如果在我的EXISTS条件中返回单行或100,000行也没关系...如果为正在运行的行返回任何结果,那么您已经通过了存在检查。那么为什么我的子查询SELECT语句返回的重要性呢?

-------------------- EDIT ---------------------- < / strong>

根据请求,下面是我在TOAD中运行的执行计划...请注意我在上面的示例中编辑了表名以方便 - 在这些计划中ALSS_SALES2 =销售额高于SALESEXT_TMP = tempTABLE。

也应该提到但是这两个表中都没有索引。我还没有将它们添加到我的tempTable中,我正在测试一个只包含字段和数据的销售表的廉价副本但没有指数,约束或安全。

感谢大家的帮助!

查询1执行计划

Query1 Execution Plan

查询2执行计划

Query2 Execution Plan

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

问题

1)为什么rownum的调用会导致执行计划发生变化?

2)过滤器的效率如此令人难以置信?

3)我是否遗漏了Exists子句导致此更改的方式的基本原因?

2 个答案:

答案 0 :(得分:8)

发布实际的查询计划会非常有帮助。

通常,当优化器看到带有rownum的子查询时,从根本上限制了它转换查询的能力,并将子查询的结果与主查询合并,因为这样做可能会影响结果。这可能是一种强制Oracle实现子查询的快速方法,如果它恰好比优化器选择的计划更有效。但是,在这种情况下,它可能会导致优化器放弃一个使查询更有效的转换步骤。

有时,您会看到有人接受

之类的查询
SELECT b.*
  FROM (SELECT <<columns>>
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id

并将rownum添加到a子查询

SELECT b.*
  FROM (SELECT <<columns>>, rownum
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id

为了强制优化器在执行连接之前评估a子查询。当然,通常情况下,如果效率更高,优化器应默认执行此操作。但是如果优化器出错了,添加rownum可能比找出正确的提示集来强制计划或深入挖掘底层问题以找出正确的解决方案更快。

当然,在WHERE EXISTS中有一个子查询的特定情况下,rownum列表中只有SELECT的使用,我们人类可以检测到rownum 1}}不应该阻止优化器会关心使用的任何查询转换步骤。但是,优化器可能正在使用更通用的规则,即必须完全执行引用类似rownum的函数的子查询(这可能取决于确切的Oracle版本和/或优化程序设置)。所以优化器实际上做了很多额外的工作,因为它不够聪明,无法识别你添加的rownum不会影响查询的结果。

答案 1 :(得分:0)

只是一个问题,这个查询的执行计划是什么:

UPDATE sales s
   SET status = 'DONE', trandate = sysdate
 WHERE EXISTS (Select NULL
 FROM tempTable tmp
     WHERE s.key1 = tmp.key1
       AND s.key2 = tmp.key2
       AND s.key3 = tmp.key3);

它可视化EXISTS (...)表达式所需的内容 - 实际上什么都没有!如前所述,Oracle只需检查是否返回任何内容,而不是在子查询中返回