以下查询背后的机制有什么解释?
它看起来像是对表进行动态过滤的强大方法。
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
)
答案 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字段。