问题
我试图理解为什么这两个 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执行计划
查询2执行计划
------------------------------------------------
问题
1)为什么调用rownum会导致执行计划发生变化?
2) 过滤器的效率如此之低是怎么回事?
3) 我是否遗漏了导致这种变化的 Exists 子句的工作方式的一些基本内容?