为什么即使不应该执行,T-SQL块也会给出错误?

时间:2020-03-06 14:35:56  来源:igfitidea点击:

我正在编写一个(貌似)简单明了的SQL代码段,该代码段在确保该列存在之后将其删除。
问题是:如果该列不存在,则IF子句中的代码会抱怨找不到该列!好吧,这就是为什么它位于IF子句中的原因!
所以我的问题是,为什么不应该执行的一段代码会出现错误?

这是代码段:

IF exists (select * from syscolumns
    WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
    ALTER TABLE [dbo].[Table_MD]
        DROP COLUMN timeout
END
GO

...这是错误:

`执行SQL脚本[...]时出错。无效的列名"超时"

我正在使用Microsoft SQL Server 2005 Express Edition。

解决方案

它可能永远不会执行,但是它会被Sql Server解析以确保有效性。 "解决"的唯一方法是构造一个动态sql块,然后有选择地执行它

IF exists (select * from syscolumns
    WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
    DECLARE @SQL nvarchar(1000)
    SET @SQL = N'ALTER TABLE [dbo].[Table_MD] DROP COLUMN timeout'
    EXEC sp_executesql @SQL
END
GO

原因:
当Sql Server编译代码时,他们将检查是否已使用了对象(如果存在)。此检查过程将忽略任何" IF"," WHILE"等构造,并仅检查代码中所有使用的对象。

这是我如何使其工作的方法:

在IF子句中,我用exec('ALTER ... DROP ...')更改了ALTER ... DROP ...`命令。

似乎SQL Server在解析代码时会对代码进行有效性检查,并发现不存在的列在某处被引用(即使该代码段将永远不会执行)。
使用exec(ute)命令将有问题的代码包装在字符串中,解析器不会抱怨,并且仅在必要时执行代码。
这是修改后的代码段:

IF exists (select * from syscolumns
    WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
    exec ('ALTER TABLE [dbo].[Table_MD] DROP COLUMN timeout')
END
GO

顺便说一句,Oracle中有一个类似的问题,并且使用"立即执行"子句也有一个类似的解决方法。