我有一些似乎有效的T-SQL难题,但我想知道是否有人可以试图告诉我这里发生的事情。请考虑以下脚本:
SELECT *
FROM TableA a
WHERE a.CustomerID NOT IN (SELECT b.CustomerID FROM TableB b WHERE a.CustomerID = b.CustomerID AND a.WorkOrder = b.WorkOrder)
AND a.[Date] > DATEADD(DD,-3,GETDATE())
对于编译器如何不对此脚本进行内爆,我感到非常难过。如何在引用外部查询的子查询中选择NOT IN的位置?获取TableA的内容,其中CustomerID不是来自TableB等的CustomerID ...但是当它在子查询中找到匹配的CustomerID时,NOT IN启动并阻止记录显示在外部查询选择中。我猜这是编译器停止的地方。但是因为没有选择特定的CustomerID,它无法加入内部查询,因此内部查询不会选择该CustomerID,然后允许外部查询选择该记录?是?没有?掉下兔子洞?有没有更好的方法来写这个?
如果有人可以详细说明这里发生的事情,或者引用可以解释的内容,我们将不胜感激。我真的找不到任何人解释这个过程,也许没有使用正确的搜索词。
谢谢!
答案 0 :(得分:3)
它被称为“Correlated subquery”和“子查询可以为外部查询处理的每一行评估一次”。
所以在这里,对于TableA的每一行,子查询从TableB中寻找匹配数据并确定是否满足NOT IN条件。然后转到TableA中的下一行重复该循环,直到评估了TableA的所有相关行。
当您加入2个表时,另一种方法可能是“左排除连接”,但忽略存在连接的行。
SELECT
*
FROM TableA a
LEFT JOIN TableB b ON a.CustomerID = b.CustomerID
AND a.WorkOrder = b.WorkOrder
WHERE b.CustomerID IS NULL
AND a.[Date] > DATEADD(DD, -3, GETDATE())
;
或使用NOT EXISTS的其他“半连接”替代方案:
SELECT
*
FROM TableA a
WHERE NOT EXISTS (
SELECT NULL
FROM TableB b
WHERE a.CustomerID = b.CustomerID
AND a.WorkOrder = b.WorkOrder
)
AND a.[Date] > DATEADD(DD, -3, GETDATE())
;
请注意,子查询用于| NOT | EXISTS不必通过select子句返回任何值。有些人喜欢在使用EXISTS时使用“select 1”或“select *”,但实际上使用哪个并不重要。使用“select NULL”是我的首选。
您可以通过检查执行计划了解有关这些替代方案的更多信息,例如,请参阅http://sqlfiddle.com/#!6/04064/2。