2

以下查询背后的机制是什么?

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

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
  )

在 SQL Fiddle 上对其进行现场测试

4

3 回答 3

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
于 2012-07-21T12:41:34.390 回答
1

让我们把它翻译成英文:

“Select rows from tblwhere tblhas a row of the same IDand greater amt.”

这样做是选择最大值为的行amt之外的所有内容ID

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

于 2012-07-21T11:34:17.157 回答
0

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

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

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

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

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

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

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

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

于 2012-07-21T13:44:10.390 回答