w00*_*977 3 sql sql-server sql-server-2008
请参阅下面的表格结构:
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)
Run Code Online (Sandbox Code Playgroud)
为什么以下查询返回两个结果:
select * from Person WHERE id IN (SELECT ID FROM [Order])
Run Code Online (Sandbox Code Playgroud)
订单中不存在ID.为什么上面的查询会产生结果?我希望它会出错,因为我不按顺序存在.
Aar*_*and 11
这种行为虽然不直观,但在Microsoft的知识库中有很好的定义:
从那篇文章:
为了说明该行为,请使用以下两个表结构和查询:
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)
Run Code Online (Sandbox Code Playgroud)
查询返回一个结果,其中从表X1中考虑列ColB.
通过限定列名称,将出现错误消息,如以下查询所示:
SELECT ColA FROM X1 WHERE ColA in (Select X2.ColB FROM X2)
Run Code Online (Sandbox Code Playgroud)
服务器:消息207,级别16,状态3,行1
无效的列名称"ColB".
人们多年来一直在抱怨这个问题,但微软并不打算解决这个问题.毕竟,它符合标准,主要表明:
如果在当前作用域中未找到列x,则遍历到下一个外部作用域,依此类推,直到找到引用.
以下更多信息连接"错误"以及多个官方确认,这种行为是设计的,不会改变(所以你必须改变你的 - 即总是使用别名):
连接#
338468:
未验证子查询中的CTE列名称解析连接#735178:在某些情况下,当使用IN运算符时,T-SQL子查询不起作用
连接#302281:不存在的列导致忽略子查询
连接#772612:别名错误在IN运算符内未报告
连接#265772:使用子选择的错误
在您的情况下,如果您使用比ID,OID和PID更有意义的名称,则可能不太可能发生此"错误".是否Order.PID指向Person.id或Person.PID?设计您的表格,以便人们可以在不必询问您的情况下找出关系.A PersonID应该始终是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?
);
Run Code Online (Sandbox Code Playgroud)
这里的问题是你没有Table.Column在你的子查询中使用表示法,表Order没有列ID,ID而子查询确实意味着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)
Run Code Online (Sandbox Code Playgroud)
第一个将执行但将返回不正确的结果,第二个将引发错误.这是因为外部查询的列可以在子查询中引用,因此在这种情况下,您可以使用Person子查询中的列.也许你想使用这样的查询:
select * from Person WHERE pid IN (SELECT PID FROM [Order])
Run Code Online (Sandbox Code Playgroud)
但是你永远不知道[Order]表的模式何时发生变化,如果有人PID从那里删除了列,[Order]你的查询将返回表中的所有行Person.因此,请使用别名:
select * from Person as P WHERE P.pid IN (SELECT O.PID FROM [Order] as O)
Run Code Online (Sandbox Code Playgroud)
快速注意 - 这不是SQL Server特有的行为,它是标准的SQL: