2017-08-10 91 views
1

我有一点似乎正在工作的T-SQL难题,但我想知道是否有人可以尝试给我一个故障,以了解这里发生的事情。考虑以下脚本: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?从TableB等获取TableA的CustomerID不在CustomerID中的内容...但是,如果在子查询中找到匹配的CustomerID,则NOT IN会启动并阻止记录显示在外部查询选择中。我猜这是编译器停止的地方。但是由于没有选择特定的CustomerID,它不能加入内部查询,因此内部查询不会选择该CustomerID,然后允许外部查询选择该记录?是?没有?落下兔子洞?有没有更好的方式来写这个?

希望有人能详细说明这里发生的事情,或引用可以解释的事情。我无法找到任何人解释这个过程,也许没有使用正确的搜索条件。

谢谢!

回答

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 *”,但事实上,使用哪一个并不重要。使用“选择NULL”是我的首选。

您可以通过检查执行计划来了解更多关于这些替代方法的信息,例如参见http://sqlfiddle.com/#!6/04064/2

原始查询:enter image description here “左排除加入”另类:enter image description here “不存在”的选择:enter image description here

+0

谢谢这有助于澄清,我不知道有它的名称!你能解释一些关于WHERE NOT EXISTS(SELECT NULL)吗?如果子查询返回一个NULL,那么会被认为是“EXISTING”呢?我喜欢这种方法,因为我的TableA select绝对不能因为连接问题返回任何dups ,TableA和TableB实际上有大约300列,并且是直接连接在一起时经常不可信的客户数据。 – Xenophage

+1

我已经添加了一个关于“select NULL”的注释,我可以说没关系,重点是它是视为*“几乎是一个连接”*(事实上是“左反半连接”),因此子查询的select子句并未真正考虑,只是where子句足以确定匹配是否存在。 :请注意,“left excluded join”不会乘以结果的行,因为只返回UNMATCHED行。 –