SQL存在为什么选择rownum会导致执行计划效率低下?
问题 我试图理解为什么这两个Oracle语法更新查询中的一个微小差异会导致完全不同的执行计划 问题1:SQL存在为什么选择rownum会导致执行计划效率低下?,sql,oracle,exists,query-performance,rownum,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 *
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-从两个表中提取总结果,并使用排序和哈希连接返回结果。这种形式良好,代价为2346(尽管使用了EXISTS子句和内聚子查询)
- Query2-同时提取两个表结果,但使用计数和筛选器来完成相同的任务,并返回一个执行计划,成本高达惊人的77789696!我应该注意到,他的查询只是挂在我的头上,所以我并不确定这会返回相同的结果(尽管我认为应该)
3) Exists子句的工作方式是否遗漏了导致此更改的基本要素?发布实际的查询计划将非常有用 但是,通常情况下,当优化器看到子查询具有
rownum
时,会从根本上限制其转换查询和将子查询的结果与主查询合并的能力,因为这样做可能会影响结果。如果子查询恰好比优化器选择的计划更有效,那么这可能是迫使Oracle实现子查询的一种快速方法。不过,在这种情况下,它可能会导致优化器放弃转换步骤,从而使查询更高效
偶尔,你会看到有人接受这样的询问
SELECT b.*
FROM (SELECT <<columns>>
FROM driving_table
WHERE <<conditions>>) a,
b
WHERE a.id = b.id
以强制优化器在执行联接之前计算a
子查询。当然,通常情况下,如果优化器效率更高,优化器应该在默认情况下这样做。但是,如果优化器出错,添加rownum
可能比找出正确的提示集来强制执行计划或深入底层问题以找到正确的解决方案要快
当然,在特定的情况下,您在
存在的地方有一个子查询,其中rownum
的唯一用法出现在SELECT
列表中,我们可以检测到rownum
不应该阻止优化器想要使用的任何查询转换步骤。不过,优化器可能使用了一个更一般的规则,即引用函数rownum
的子查询必须完全执行(这可能取决于Oracle的确切版本和/或优化器设置)。因此,优化器实际上在做大量额外的工作,因为它不够聪明,无法识别您添加的rownum
不可能影响查询结果。只是一个问题,此查询的执行计划是什么:
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);
它可视化了存在(…)
表达式中需要的内容-实际上什么都没有!如前所述,Oracle只需检查是否返回了任何内容,而不是子查询中返回的内容 我的假设是,当您使用*时,引擎使用最相关的索引,但仅返回rownum可能会忽略该索引并执行顺序计划。但是我不确定你能不能包括执行计划,包括访问和过滤部分?谢谢Justin,这实际上很有意义,给了我很多关于rownum的好信息。我必须记住将rownum添加到子查询以尽早实现它的技巧。谢谢
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);