请参阅下面的表结构:
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。为什么上面的查询会产生结果?我希望它会出错,因为我不按顺序存在。
答案 0 :(得分:11)
这种行为虽然不直观,但在微软的知识库中有很好的定义:
KB #298674 : PRB: Subquery Resolves Names of Column to Outer Tables
从那篇文章:
为了说明该行为,请使用以下两个表结构和查询:
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,则遍历到下一个外部作用域,依此类推,直到找到引用。
以下更多信息连接“错误”以及多个官方确认,这种行为是设计的,不会改变(所以你必须改变你的 - 即总是使用别名 ):
Connect #338468 : CTE Column Name resolution in Sub Query is not validated
Connect #735178 : T-SQL subquery not working in some cases when IN operator used
Connect #302281 : Non-existent column causes subquery to be ignored
Connect #772612 : Alias error not being reported when within an IN operator
Connect #265772 : Bug using sub select
在您的情况下,如果使用比ID,OID和PID更有意义的名称,则可能不太可能发生此“错误”。 Order.PID
是否指向Person.id
或Person.PID
?设计您的表格,以便人们可以在不必询问您的情况下找出关系。 PersonID
应始终为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?
);
答案 1 :(得分:9)
这里的问题是您没有在子查询中使用Table.Column
表示法,表Order
没有列ID
,而子查询中的ID
实际上意味着{ {1}},而不是Person.ID
。这就是为什么我总是坚持在生产代码中使用表的别名。比较这两个查询:
[Order].ID
第一个将执行但会返回不正确的结果,第二个将引发错误。这是因为外部查询的列可以在子查询中引用,因此在这种情况下,您可以在子查询中使用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)
列。
也许你想使用这样的查询:
Person
但是你永远不知道select * from Person WHERE pid IN (SELECT PID FROM [Order])
表的模式何时发生变化,如果有人从[Order]
中删除了列PID
,那么你的查询将返回表{{1}中的所有行}}。因此,请使用别名:
[Order]
快速记下 - 这不是SQL Server特有的行为,它是标准的SQL:
答案 2 :(得分:0)
订单表没有id列请改为尝试这些:
select * from Person WHERE id IN (SELECT OID FROM [Order])
OR
select * from Person WHERE pid IN (SELECT PID FROM [Order])