如何在SQL Server中更新表格,如果列存在?

8

我有一个名为 MyTable 的表格,是通过以下方式创建的

CREATE TABLE MyTable
(
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [Type] [int] NOT NULL,
    [CreatedDate] [datetime] NOT NULL,
    [ModifiedDate] [datetime] NOT NULL,
)

我想要检查一张表中是否存在某列,如果存在,就将数据复制到另一列中,然后删除旧的列,操作如下:

IF (SELECT COLUMNPROPERTY(OBJECT_ID('MyTable'), 'Timestamp', 'Precision')) IS NOT NULL
BEGIN
    UPDATE [dbo].[MyTable]
    SET [CreatedDate] = [Timestamp]

    ALTER TABLE [dbo].[MyTable]
    DROP COLUMN [Timestamp]
END
GO

然而,当我尝试运行它时,我遇到了一个错误:
Invalid column name 'Timestamp'

我该如何完成我想做的事情?

那么 select columnproperty(...) 子查询返回什么?我猜它不是空值,所以你的更新运行了,由于时间戳确实不存在,所以你得到了那个错误。 - Marc B
为什么使用MySQL标签,你的代码似乎是SQL Server的,请删除不合适的标签。 - ScaisEdge
对我来说,这似乎是一种野蛮西部的编程方式。 - Drew
@MarcB 它返回为 NULL。 - Sean Smyth
1
当存储过程本身被编译时,其中的 SQL 语句会被评估其正确性。这意味着您的 if() 在存储过程解析并检查表中是否有 Timestamp 字段时不会起作用。您必须使用动态 SQL 来完成。例如:sql = 'UPDATE ..'; exec @sql 这样的东西。 - Marc B
显示剩余3条评论
2个回答

14

这是一个编译问题。

如果在编译批处理时表不存在,那么所有引用该表的语句都将被推迟编译,一切工作正常。但是对于已经存在的表,您会遇到此问题,因为它尝试编译所有语句,并且因为不存在的列而停顿。

您可以将代码推入子批处理中,以便只有在访问该分支时才编译。

IF (SELECT COLUMNPROPERTY(OBJECT_ID('MyTable'), 'Timestamp', 'Precision')) IS NOT NULL
BEGIN
EXEC('
    UPDATE [dbo].[MyTable]
    SET [CreatedDate] = [Timestamp]

    ALTER TABLE [dbo].[MyTable]
    DROP COLUMN [Timestamp]
    ')
END
GO

如果你只是想重命名列

EXEC sys.sp_rename 'dbo.MyTable.[TimeStamp]' , 'CreatedDate', 'COLUMN'

不过,如果从一个 CreatedDate 列不存在的位置开始会更容易。


0

您需要使用ALTER TABLE语句首先创建[Timestamp]列。

然后其余部分应该可以运行。

根据评论编辑(我知道这些信息在SO的其他地方也有,但我找不到):

很遗憾,在SQL Server中,IF条件不允许您忽略无法解析的代码。发生的情况是,SQL Server查看您的命令,并解析每个语句以确保它是有效的。

当它这样做时,SQL Server并不聪明到足以弄清楚如果没有[TimeStamp],则无法到达需要存在[TimeStamp]的UPDATE语句的无效语句。换句话说,即使您将该命令嵌套在不会被执行的IF条件中,也不能编写期望不存在的列的SQL命令。在测试IF条件之前,SQL Server将解析整个语句并禁止其运行。

常用的解决方法是使用动态SQL,因为SQL Server无法预解析它,所以它不会尝试。


2
重点是检查我的系统是否已被更改以删除此列。重新添加它不是重点。如果它仍然存在,那意味着我的脚本以前没有运行过。如果该列不存在,则希望它跳过此整个代码块。 - Sean Smyth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接