3

请看下表结构:

CREATE TABLE Person (id int not null, PID INT NOT NULL, Name VARCHAR(50))
CREATE TABLE [Order] (OID INT NOT NULL, PID INT NOT NULL)

INSERT INTO Person VALUES (1,1,'Ian')
INSERT INTO Person VALUES (2,2,'Maria')
INSERT INTO [Order] values (1,1)

为什么下面的查询会返回两个结果:

select * from Person WHERE id IN (SELECT ID FROM [Order])

订单中不存在 ID。为什么上面的查询会产生结果?我希望它会出错,因为我不会按顺序存在。

4

3 回答 3

11

这种行为虽然不直观,但在 Microsoft 的知识库中有很好的定义:

KB #298674:PRB:子查询将列名解析为外部表

从那篇文章:

为了说明该行为,请使用以下两个表结构和查询:

CREATE TABLE X1 (ColA INT, ColB INT)
CREATE TABLE X2 (ColC INT, ColD INT)
SELECT ColA FROM X1 WHERE ColA IN (Select ColB FROM X2)

该查询返回一个结果,其中从表 X1 中考虑列 ColB。

通过限定列名,会出现错误消息,如以下查询所示:

SELECT ColA FROM X1 WHERE ColA in (Select X2.ColB FROM X2)

服务器:消息 207,级别 16,状态 3,第 1 行
无效的列名称“ColB”。

多年来,人们一直在抱怨这个问题,但微软不会修复它。毕竟,它符合标准,该标准基本上规定:

如果在当前范围内没有找到 x 列,则遍历到下一个外部范围,依此类推,直到找到引用。

以下连接“错误”中的更多信息以及多个官方确认此行为是设计使然并且不会更改(因此您必须更改您的 - 即始终使用别名):

连接 #338468:未验证子查询中的 CTE 列名称解析
连接 #735178:使用 IN 运算符时,T-SQL 子查询在某些情况下
不起作用连接 #302281:不存在的列导致子查询被忽略
连接 #772612:别名错误在 IN 运算符
连接时未报告 #265772:使用子选择的错误

在您的情况下,如果您使用比 ID、OID 和 PID 更有意义的名称,则此“错误”可能不太可能发生。是否Order.PID指向Person.idPerson.PID?设计您的表格,以便人们无需询问您就可以找出关系。APersonID应该始终是 a PersonID,无论它在架构中的哪个位置;与OrderID. 为完全模棱两可的模式付出代价并不是节省几个打字字符的好代价。

你可以写一个EXISTS子句:

... FROM dbo.Person AS p WHERE EXISTS 
(
  SELECT 1 FROM dbo.[Order] AS o
  WHERE o.PID = p.id -- or is it PID? See why it pays to be explicit?
);
于 2013-09-03T02:13:28.533 回答
9

The problem here is that you're not using Table.Column notation in your subquery, table Order doesn't have column ID and ID in subquery really means Person.ID, not [Order].ID. That's why I always insist on using aliases for tables in production code. Compare these two queries:

select * from Person WHERE id IN (SELECT ID FROM [Order]);

select * from Person as p WHERE p.id IN (SELECT o.ID FROM [Order] as o)

The first one will execute but will return incorrect results, and the second one will raise an error. It's because the outer query's columns may be referenced in a subquery, so in this case you can use Person columns inside the subquery. Perhaps you wanted to use the query like this:

select * from Person WHERE pid IN (SELECT PID FROM [Order])

But you never know when the schema of the [Order] table changes, and if somebody drops the column PID from [Order] then your query will return all rows from the table Person. Therefore, use aliases:

select * from Person as P WHERE P.pid IN (SELECT O.PID FROM [Order] as O)

Just quick note - this is not SQL Server specific behaviour, it's standard SQL:

于 2013-09-02T18:39:19.353 回答
0

订单表没有 id 列

试试这些:

select * from Person WHERE id IN (SELECT OID FROM [Order])

或者

select * from Person WHERE pid IN (SELECT PID FROM [Order])
于 2013-09-02T16:44:13.003 回答