我正在调试其他人的查询,并且遇到了一个非常奇怪的声明,看起来它应该根本不起作用。我将其从原始查询中提炼为:
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的答案。
答案 0 :(得分:3)
我不确定原因,但是当子查询调用不正确的列并且基本上无效时,外部select语句仍会运行。具有子查询的in / not in谓词基本上被忽略。我以前见过这个,但从来没有找到原因。不过我只是环顾四周,找到了这个链接:
有人提到以下内容:
我同意这种行为令人困惑,但它是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服务器中插入一条或多条记录才会返回任何内容。