Sql server 测试列存在、添加列和更新列

Sql server 测试列存在、添加列和更新列,sql-server,tsql,ddl,Sql Server,Tsql,Ddl,我正在尝试编写SQL Server数据库更新脚本。我想测试表中是否存在列,如果不存在,则添加具有默认值的列,最后根据同一表中不同列的当前值更新该列。我希望这个脚本可以运行多次,第一次更新表和后续运行时应该忽略该脚本。我的脚本当前看起来如下所示: IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable

我正在尝试编写SQL Server数据库更新脚本。我想测试表中是否存在列,如果不存在,则添加具有默认值的列,最后根据同一表中不同列的当前值更新该列。我希望这个脚本可以运行多次,第一次更新表和后续运行时应该忽略该脚本。我的脚本当前看起来如下所示:

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable')
BEGIN

ALTER TABLE [dbo].[PurchaseOrder] ADD [IsDownloadable] bit NOT NULL DEFAULT 0

UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref] IS NOT NULL

END

SQL Server返回错误无效的列名“IsDownloadable”,即我需要提交DDL才能更新列。我尝试过各种排列,但进展不快。

尝试在ALTER表后添加GO语句

这对我来说是个新闻,但它说一批中所有在GO之前的语句都被编译成一个查询计划。如果不使用SQL,整个计划实际上就是一个查询

编辑:因为GO给出了一个语法错误,这对我来说似乎很奇怪,所以我创建了一些类似的东西,并发现这是可行的

declare @doUpdate bit;

SELECT @doUpdate = 0;

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable')
BEGIN
 SELECT @doUpdate=1
END

IF @doUpdate<>0 
   ALTER TABLE [dbo].[PurchaseOrder] ADD [IsDownloadable] bit NOT NULL DEFAULT 0

IF @doUpdate<>0
  UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref]=0

COMMIT TRAN

除非列已经存在,否则此脚本将无法成功运行,而此时您不需要它

SQL脚本在执行之前必须经过解析。如果在解析脚本时该列不存在,则解析将失败。以后脚本创建列并不重要;解析器无法知道这一点

如果要访问刚添加的列,需要放入GO语句批处理分隔符。但是,一旦这样做,就不能再维护上一批中的任何控制流或变量,就像运行两个单独的脚本一样。这使得同时有条件地执行DDL和DML变得很棘手

最简单的解决方法是使用动态SQL,解析器在运行时之前不会尝试解析动态SQL,我可能会向您推荐,因为您的DML不是很复杂:

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable')
BEGIN

    ALTER TABLE [dbo].[PurchaseOrder] ADD 
        [IsDownloadable] bit NOT NULL DEFAULT 0

    EXEC sp_executesql
        N'UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref] IS NOT NULL'

END

我自己也经常被这个问题困扰,不幸的是,当涉及@parameters和“string”时,中建议的解决方案很快就会变得混乱。然而,我通过利用同义词的用法找到了一个不同的解决方法:

IF(COL_LENGTH('MyTable', 'NewCol') IS NULL)
BEGIN
    ALTER TABLE MyTable ADD NewCol VARCHAR(16) NULL;

    CREATE SYNONYM hack FOR MyTable;
    UPDATE hack SET NewCol = 'Hello ' + OldCol;
    DROP SYNONYM hack;

    ALTER TABLE MyTable ALTER COLUMN NewCol VARCHAR(16) NOT NULL;
END

虽然接受的答案确实有效,但对于更复杂的情况,可以使用临时表将数据保存到GO语句之后。只是要确保你记得清理后

例如:

-- Create a tempTable if it doesn't exist. Use a unique name here
IF OBJECT_ID('tempdb..#tempTable') IS NOT NULL DROP TABLE #tempTable
CREATE TABLE #tempTable (ColumnsCreated bit)

-- Create your new column if it doesn't exist. Also, insert into the tempTable.
IF NOT EXISTS (
    SELECT * FROM   INFORMATION_SCHEMA.COLUMNS 
    WHERE  TABLE_NAME = 'targetTable' AND COLUMN_NAME = 'newColumn')
BEGIN
    INSERT INTO #tempTable VALUES (1)

    ALTER TABLE .dbo.targetTable ADD newColumn [SMALLINT] NULL ;
END

GO

-- If the tempTable was inserted into, our new columns were created.
IF (EXISTS(SELECT * FROM #tempTable))
    BEGIN
    -- Do some data seeding or whatever
    END

-- Clean up - delete the tempTable.
IF OBJECT_ID('tempdb..#tempTable') IS NOT NULL DROP TABLE #tempTable

如果您至少使用SQL Server 2008,则可以在添加列时指定WITH值,这将使用该属性的默认值填充现有记录

IF COL_LENGTH('[dbo].[Trucks]', 'Is4WheelDrive') IS NULL
BEGIN

    ALTER TABLE [dbo].[Trucks]
    ADD [Is4WheelDrive] BIT NULL DEFAULT 1
    WITH VALUES;

END

如果[dbo].[Trucks]表中不存在新列[Is4WheelDrive],则会将该列添加到该表中。如果添加新列,将使用默认值填充现有记录,在本例中,默认值为位值1。如果列已存在,则不会修改任何记录。

在“0”附近的语法不正确。这是我已经尝试过的排列方式之一,也是我一直在寻找的。谢谢。对于更复杂的情况,可以使用临时表来保存GO语句之后的数据。只是要确保你记得清理后。