嵌套select *的混乱性能增益不存在

时间:2012-08-03 14:07:51

标签: sql performance oracle

我要查询的两个表每个都有大约1.5亿行。

以下声明我在45分钟没有返回后终止,所以我不知道它会运行多长时间:

select * from Cats cat  
where not exists( select dog.foo,dog.bar from Dogs dog
                  where cat.foo = dog.foo   
                  and cat.bar = dog.bar);

然而,此查询在大约3分钟内执行:

select * from Cats outside  
   where not exists(select * from Cats cat  
                     where exists( select dog.foo,dog.bar from Dogs dog
                      where cat.foo = dog.foo   
                      and cat.bar = dog.bar)));

我的问题是我看到这种性能提升的幕后情况是什么?

返回相同结果集后的推理:

第一个查询(慢)表示根据Cats表提供所有不存在的元素。

第二个查询(快速)表示从存在的Cats子集中提供不存在的所有元素。

我希望以下查询:

select dog.foo,dog.bar from Dogs dog
                          where cat.foo = dog.foo   
                          and cat.bar = dog.bar  

返回[A,B,C]

这两种功能都很常见。

我的猫表有以下内容:[A,B,C,D,E]

我希望以下查询:

 select * from Cats cat  
                     where exists

返回[A,B,C] 最后一块:

select * from Cats outside  
       where not exists

返回[D,E]

更新

设置符号以数学方式证明我的主张(如果我使用了错误的符号,请纠正我):

∀ Cat (Ǝ cat ≠ Ǝdog)    

对于Cat中的所有元素,返回包含cat的每个元素的集合,该集合不等于dog

中的元素
∀ Cat (Ǝ cat = Ǝdog)   

对于Cat中的所有元素,返回包含cat的每个元素的集合,该元素与dog

中的元素相同
∀ Cat (Ǝ innerCat ≠ Ǝcat)  

对于Cat中的所有元素,返回包含内部cat的每个元素的集合,该集合不等于cat中的元素

第二次更新

我看到我的数学不符合我的SQL。

5 个答案:

答案 0 :(得分:3)

显然,NOT IN和NOT EXISTS对于数据引擎进行优化是有问题的。从技术上讲,这些被称为反连接(区别于等连接,半连接,非等连接等)。

当连接难以优化时,引擎会采用嵌套循环连接。这些通常是性能最差的类型(尽管在SQL Server执行计划中,这些通常看起来相同,因为SQL Server在执行计划中调用索引查找“嵌套循环”)。

这两个查询有什么区别?第一个只有一个NOT EXISTS,所以它可能做了一些低效的事情。第二个是在最内层子查询上做一个EXISTS。首先优化,基本上作为连接。如果键有索引一切都很好。 SQL Server还可以为这些算法选择基于散列或基于合并的算法。

第二个版本中的“不存在”基于同一个表。这可能会为SQL Server提供更多优化空间。

最后,第二个版本可能会大大减少数据集。如果是这样,即使外部的嵌套循环连接也可能更快。

答案 1 :(得分:2)

第二个查询在执行时更加优化,这就是原因:

您将外部查询的Cats表别名为outside,但您未在outside的子句中引用where not exists。因此,SQL可以执行以下操作:

  • 找到cat.foo = dog.foo and cat.bar = dog.bar(来自您最里面的查询)
  • 的任何一只猫
  • 这意味着确实存在符合where not exists
  • 中所有猫的外部outside 的猫
  • 因此,对于where not exists
  • 中的所有行,false子句为outside
  • 因此查询结果为空

您的第一个查询必须为表中的每只猫重新执行嵌套查询,因此速度较慢。

答案 2 :(得分:1)

您的问题的答案是检查执行计划。

作为旁注,您应该尝试此等效查询(另请参阅https://stackoverflow.com/a/1069467/44522):

SELECT * FROM Cats cat LEFT OUTER JOIN Dogs dog 
    ON cat.foo = dog.foo and cat.bar = dog.bar
WHERE dog.foo IS NULL and dog.bar IS NULL

我敢打赌它会更快地执行(假设你得到了正确的索引)。

答案 3 :(得分:0)

我通过测试发现,这是在初始问题中执行查询的最有效方式:

Select cat.foo,cat.bar from cats cat

MINUS

Select dog.foo,dog.bar from dogs dog

这是有效的,因为我的列都不可为空。

答案 4 :(得分:-1)

他们是具有不同结果的不同查询。要使第二次返回与第一次相同,它需要像......

   select * from cats outside   
   where not exists(select * from Cats cat   
                     where exists( select dog.foo,dog.bar from Dogs dog 
                      where cat.foo = dog.foo    
                      and cat.bar = dog.bar)
                      and outside.foo = cat.foo
                      and outside.bar=cat.bar
                      )