SQL临时表在应该抛出无效列名错误时没有抛出



我在SQL Server 2008 Management Studio中发现了以下问题。当我完整运行以下脚本时,我预计会出现错误(由于"复制和粘贴错误"),但没有收到它们。

IF OBJECT_ID('Foo') IS NOT NULL
BEGIN
    DROP TABLE Foo
END
IF OBJECT_ID('Bar') IS NOT NULL
BEGIN
    DROP TABLE Bar
END
CREATE TABLE Foo (
    FooID int
)
Create Table Bar (
    BarID int
,   FooID int
)
INSERT INTO Foo
SELECT 1 UNION ALL SELECT 2
INSERT INTO Bar
SELECT 1,1 UNION ALL SELECT 2,1 UNION ALL SELECT 3,1
GO
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
BEGIN
      DROP TABLE #TEMP
END
GO
CREATE TABLE #TEMP (
    FooID int
)
INSERT INTO #TEMP
    SELECT FooID FROM Bar
GO
SELECT * FROM Foo WHERE FooID IN (SELECT FooID FROM #TEMP)
GO
SELECT * FROM Bar WHERE BarID IN (SELECT BarID FROM #TEMP)
GO
SELECT * FROM #TEMP
GO

最后第二条包含where子句过滤器的语句运行了,但是在#TEMP中没有BarID列。当脚本作为一个整体运行时,我没有收到任何错误,脚本返回Foo中的所有行。但是,当我单独运行该命令时,我得到错误提示,告诉我在#TEMP中没有BarId。

这有什么原因吗?是我的代码吗?

您的倒数第二个查询实际上正在正常工作。

其他评论请纠正我,但我理解这是一个相关的子查询。表混叠将显示实际发生的情况。您的查询相当于:

SELECT * FROM Bar x WHERE x.BarID IN (SELECT x.BarID FROM #TEMP)

对于#TEMP中的每一行,嵌套的select实际上是从表Bar中返回BarID的当前值,所以是的,BarID in (BarID)为真,所以Bar中的每一行都匹配,所以每一行都返回。

为了表明你没有疯,试着

SELECT * FROM Bar WHERE BarID IN (SELECT NonExistentFieldName FROM #TEMP) 

这将引发我认为您期望的错误。

有关Neil回答的背景信息,请参阅以下KB文章:

http://support.microsoft.com/kb/298674

他们使用的示例(虽然看起来不应该工作)与您的示例非常相似(尽管没有#temp表):

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)

它们还显示,添加表别名(从许多原因来看,这是一种很好的做法,但会防止这个问题通过编译)会使查询像您最初预期的那样失败。

这实际上是遵循ANSI标准的列分辨率,并且不会改变(除非他们实现了英国的SET STRICT_CHECKS ON)。

其他一些抱怨这个bug的人(以及更多关于为什么引擎必须这样工作的背景):

http://connect.microsoft.com/SQL/feedback/details/542289/

http://connect.microsoft.com/SQL/feedback/details/422252/

http://connect.microsoft.com/SQL/feedback/details/126785/

不得不同意@BalamBalam的评论:为什么你写无效的代码,然后抱怨它被错误地认为是有效的?

最新更新