在相关子查询上使用运算符EXISTS的说明

时间:2012-07-21 11:27:09

标签: sql sql-server exists

以下查询背后的机制有什么解释?

它看起来像是对表进行动态过滤的强大方法。

CREATE TABLE tbl (ID INT, amt INT)
INSERT tbl VALUES
(1,1),  
(1,1),  
(1,2),
(1,3),
(2,3),  
(2,400),
(3,400),
(3,400)

SELECT *
FROM tbl T1
WHERE EXISTS
  (
    SELECT *
    FROM tbl T2
    WHERE 
       T1.ID = T2.ID AND
       T1.amt < T2.amt
  )

Live test of it here on SQL Fiddle

3 个答案:

答案 0 :(得分:2)

您通常可以使用显式连接将相关子查询转换为等效表达式。这是一种方式:

SELECT distinct t1.*
FROM tbl T1 left outer join
     tbl t2
     on t1.id = t2.id and
        t1.amt < t2.amt
where t2.id is null
马丁史密斯以另一种方式表现出来。

它们是否是“动态过滤的强大方式”的问题是正确的,但(通常)不重要。您可以使用其他SQL构造进行相同的过滤。

为什么要使用相关子查询?有几个正面和几个负面,一个重要原因是两者。从积极的方面来说,您不必担心行的“倍增”,如上面的查询中所发生的那样。此外,当您有其他过滤条件时,相关子查询通常更有效。并且,有时使用删除或更新,它似乎是表达查询的唯一方式。

Achilles的脚跟是许多SQL优化器将相关子查询实现为嵌套循环连接(即使不必)。因此,有时它们可​​能非常低效。但是,您拥有的特定“存在”结构通常非常有效。

此外,表之间的连接的性质可能会在嵌套子查询中丢失,这会使where子句中的条件变得复杂。在更复杂的情况下,很难理解发生了什么。

我的推荐。如果要在大型表上使用它们,请了解数据库中的SQL执行计划。相关的子查询可以带来SQL性能的最佳或最差。

可能的编辑。这更像是OP中的脚本:

SELECT distinct t1.*
FROM tbl T1 inner join
     tbl t2
     on t1.id = t2.id and
        t1.amt < t2.amt

答案 1 :(得分:1)

让我们把它翻译成英文:

“从tbl中选择行tbl有一行ID和更大amt的行。”

这样做是为每个amt选择除了最大值为ID的行以外的所有内容。

注意,最后一行SELECT * FROM tbl是一个单独的查询,可能与手头的问题无关。

答案 2 :(得分:0)

正如其他人已经指出的那样,在相关子查询中使用EXISTS本质上是告诉数据库引擎“返回所有记录,其中有相应的记录符合子查询中指定的条件”。但还有更多。

EXISTS关键字表示布尔值。它也可以表示“存在至少一条与WHERE语句中的条件匹配的记录。”换句话说,如果找到一条记录,“我已经完成了,我不需要再搜索了。”

在相关子查询中使用EXISTS可以获得的效率增益来自以下事实:一旦EXISTS返回TRUE,子查询就会停止扫描记录并返回结果。类似地,只要任何记录与子查询的WHERE语句中的条件匹配,使用NOT EXISTS的子查询就会返回。

我认为这个想法是使用EXISTS的子查询是支持的,以避免使用嵌套循环搜索。正如@Gordon Linoff所述,查询优化器可能会或可能不会按预期执行。我相信MS SQL Server通常会充分利用EXISTS。

我的理解是,并非所有查询都能从EXISTS中受益,但通常情况下,它们会受到影响,特别是在简单结构的情况下,例如在您的示例中。

我可能已经扼杀了其中一些,但从概念上讲,我相信它是在正确的轨道上。

需要注意的是,如果您有一个性能关键的查询,那么最好使用EXISTS评估版本的执行情况,其中一个版本使用简单的JOINS,正如Linoff先生所指出的那样。根据您的数据库引擎,表格结构,一天中的时间以及月亮和星星的对齐情况,它不会被切割和干燥,速度会更快。

最后一点 - 我同意lc。在子查询中使用SELECT *时,可能会取消部分或全部性能提升。仅选择PK字段。