【问题标题】:Explanation of using the operator EXISTS on a correlated subqueries在相关子查询上使用运算符 EXISTS 的说明
【发布时间】:2012-07-20 11:05:48
【问题描述】:

对以下查询背后的机制有何解释?

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

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

【问题讨论】:

  • 它从 tbl 中选择行,其中存在具有相同 id 且数量更大的另一行。即它不是与MAX(amt) 对应的ID 的一行。还有什么需要解释的?也可以使用SELECT ID,amt FROM (SELECT *, RANK() OVER (PARTITION BY ID ORDER BY amt DESC) RNK FROM tbl ) T WHERE RNK &lt;&gt; 1
  • ...不太清楚我的代码的最后一行是从哪里出现的 - 我将编辑 OP

标签: sql sql-server exists


【解决方案1】:

让我们把它翻译成英文:

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

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

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

【讨论】:

  • 最后一行只是对表tbl的单独查询,为什么会被忽略或引发错误?
  • 最后一行是错字 - 为造成的混乱道歉
  • @MartinSmith 因为没有分隔分号并且某些 RDBMS 不允许在单个语句中进行多个查询?
  • 该问题已标记为 SQL Server,因此我们知道这不会成为问题。
  • @MartinSmith 是的。抱歉,我错过了。
【解决方案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 结构进行相同的过滤。

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

阿喀琉斯之踵是许多 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

【讨论】:

  • 感谢戈登的解释。您的等效脚本与我的不等效。
  • 谢谢@whytheq。我打算添加一个“t2.id 不为空”,然后意识到内部连接做同样的事情。您的编辑完全正确。
【解决方案3】:

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

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

CAN 在相关子查询中使用 EXISTS 所带来的效率增益来自这样一个事实,即只要 EXISTS 返回 TRUE,子查询就会停止扫描记录并返回结果。同样,只要任何记录与子查询的 WHERE 语句中的条件匹配,使用 NOT EXISTS 的子查询就会返回。

我相信这个想法是使用 EXISTS 的子查询应该避免使用嵌套循环搜索。正如上面的@Gordon Linoff 所说,查询优化器可能会也可能不会按预期执行。我相信 MS SQL Server 通常会充分利用 EXISTS。

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

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

需要注意的是,如果您有一个性能关键的查询,最好使用 EXISTS 评估一个版本的执行,正如 Linoff 先生所说的那样,使用简单的 JOINS。根据您的数据库引擎、表结构、一天中的时间以及月亮和星星的对齐方式,它不是一刀切的,它会更快。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    相关资源
    最近更新 更多