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

Dan*_*anK 7 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)
Run Code Online (Sandbox Code Playgroud)

查询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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

- - - - - - - - - - 编辑 - - - - - - - - - - -

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

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

感谢大家的帮助!

查询1执行计划

Query1执行计划

查询2执行计划

Query2执行计划

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

问题

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

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

3)我是否遗漏了一些基本的东西,因为Exists子句的工作方式导致了这种变化?

Jus*_*ave 8

发布实际的查询计划将非常有帮助.

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

偶尔,你会看到有人接受查询

SELECT b.*
  FROM (SELECT <<columns>>
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id
Run Code Online (Sandbox Code Playgroud)

rownum指向a子查询

SELECT b.*
  FROM (SELECT <<columns>>, rownum
          FROM driving_table
         WHERE <<conditions>>) a,
       b
 WHERE a.id = b.id
Run Code Online (Sandbox Code Playgroud)

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

当然,在你有一个子查询的具体情况WHERE EXISTS,其中唯一使用rownum自带的SELECT列表中,我们人类可以检测到rownum不应该阻止任何查询转换步骤,优化会照顾使用.但是,优化器可能正在使用更通用的规则,即rownum必须完全执行引用类似函数的子查询(这可能取决于确切的Oracle版本和/或优化器设置).因此,优化器实际上是在做一堆额外的工作,因为它不够聪明,无法识别rownum您添加的内容不会影响查询结果.