7

问题

我试图理解为什么这两个 Oracle 语法更新查询中看似微小的差异会导致执行计划完全不同。

查询一:

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 - 从两个表中提取总结果并使用排序和哈希连接来返回结果。这以有利的 2,346 成本执行良好(尽管使用了 EXISTS 子句和内聚子查询)。

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

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

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

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

也应该提到,但此时这两个表都没有索引。我还没有将它们添加到我的 tempTable 中,我正在使用仅包含字段和数据但没有索引的 sales 表的廉价副本进行测试,约束或安全性。

感谢大家的帮助!

查询1执行计划

Query1 执行计划

查询2执行计划

Query2 执行计划

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

问题

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

2) 过滤器的效率如此之低是怎么回事?

3) 我是否遗漏了导致这种变化的 Exists 子句的工作方式的一些基本内容?

4

2 回答 2

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中唯一使用的rownumSELECT我们人类可以检测到rownum不应该阻止优化器愿意使用的任何查询转换步骤。但是,优化器可能正在使用更通用的规则,即引用类似函数的子查询rownum必须完全执行(这可能取决于确切的 Oracle 版本和/或优化器设置)。所以优化器实际上是在做一堆额外的工作,因为它不够聪明,无法识别rownum你添加的内容不可能影响查询的结果。

于 2013-12-06T22:44:37.817 回答
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 只需要检查是否返回任何内容,而不是子查询中返回的内容。

于 2013-12-07T15:54:32.887 回答