使用看起来不应该工作的IN语句,但它以某种方式执行

时间:2014-05-19 14:28:10

标签: sql sql-server tsql sql-server-2008-r2

我正在调试其他人的查询,并且遇到了一个非常奇怪的声明,看起来它应该根本不起作用。我将其从原始查询中提炼为:

DECLARE @c TABLE (id INT);
DECLARE @y TABLE (name VARCHAR(50) PRIMARY KEY);
INSERT INTO @c VALUES (1);
SELECT 
    c.*
FROM  
    @c c
WHERE 
    id NOT IN ( 
        SELECT  
            id
        FROM    
            @y
        WHERE
            id IS NOT NULL);

但这怎么可能有效呢?我添加了id为IS NOT NULL的约束,但删除它似乎不会改变行为。

你也可以删除临时表上的PRIMARY KEY,这只是一个表明执行计划以某种方式使用索引的游戏!?

这是“精简版”:

DECLARE @c TABLE (id INT);
DECLARE @y TABLE (name VARCHAR(50));
INSERT INTO @c VALUES (1);
SELECT * FROM @c WHERE id NOT IN (SELECT id FROM @y);

在SQL Server 2008 R2中执行时,将返回1的答案。

3 个答案:

答案 0 :(得分:3)

我不确定原因,但是当子查询调用不正确的列并且基本上无效时,外部select语句仍会运行。具有子查询的in /​​ not in谓词基本上被忽略。我以前见过这个,但从来没有找到原因。不过我只是环顾四周,找到了这个链接:

https://connect.microsoft.com/SQLServer/feedback/details/542289/subquery-with-error-does-not-cause-outer-select-to-fail

有人提到以下内容:

  

我同意这种行为令人困惑,但它是ANSI标准   不同范围内列名解析的行为。

     

有关详细信息,请参阅此知识库文章:   http://support.microsoft.com/kb/298674

     

这让我更加困惑的原因是我使用了列名   在 表中不存在因此得到“预期”   (列名无效)错误。所以,如果你使用的是列名   无法在内部范围内解析(SELECT Table1Id FROM Table2)   但可以在外部范围内解析(SELECT * FROM Table1 WHERE ...),   它将在该范围内得到解决和约束。

     

在您给出的示例中,这似乎令人困惑,但逻辑相同   如果在内部的WHERE子句中使用此类构造,则适用   查询。例如。考虑内部查询,如下所示:

     

...(SELECT Table2Id FROM Table2 WHERE Table2Id = Table1Id)

     

查询本身将失败,但它将起作用,因为Table1Id将是   在外部查询中绑定Table1。

答案 1 :(得分:2)

在子查询中,如果您提到的列名称不存在于构成子查询的表中,则SQL将接下来查找封闭查询中的列(除非您已经给出了别名)。

因此,子查询中提到的id实际上是id表中的@c列。

因此,对于子查询返回的每一行,将返回一次id值 - 但是假定子查询返回一个空集,这意味着NOT IN不考虑任何值,因此成功


这就是为什么在子查询中使用表别名是一个非常好的习惯 - 这样,如果你不小心命名只存在于外表中的列,你会得到一个错误而不是一个意外的结果:

SELECT * FROM @c WHERE id NOT IN (SELECT y.id FROM @y y);

产生错误。

答案 2 :(得分:0)

SELECT id FROM @y @y中没有单一值。由于@y中没有值,因此子查询不会返回id中的@c。据我所知,SQL服务器返回1是正确的。只有在@y SQL服务器中插入一条或多条记录才会返回任何内容。