我要查询的两个表每个都有大约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。
答案 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
)