存在测试列,添加列和更新列

时间:2010-05-03 23:43:05

标签: sql-server tsql ddl

我正在尝试编写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。我尝试过各种各样的排列,但是我无法快速完成。

5 个答案:

答案 0 :(得分:79)

除非该列已经存在,否则此脚本将无法成功运行,这正是需要它的时候。

必须先解析SQL脚本,然后才能执行它们。如果在解析脚本时该列不存在,则解析将失败。您的脚本稍后创建列并不重要;解析器无法知道。

如果要访问刚刚添加的列,则需要输入GO语句(批处理分隔符)。但是,一旦执行此操作,您就无法再保留上一批中的任何控制流或变量 - 就像运行两个单独的脚本一样。这使得同时有条件地同时执行DDL和DML变得棘手。

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

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

答案 1 :(得分:1)

我自己经常对这个问题感到恼火,不幸的是,Aaronaught's answer中建议的解决方案在@parameters和'字符串'时很快变得混乱。参与其中。但是,我通过利用同义词的使用找到了不同的解决方法:

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

答案 2 :(得分:0)

虽然接受的答案确实有效,但对于更复杂的情况,您可以使用临时表来保存数据超过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

答案 3 :(得分:0)

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

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

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

END

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

答案 4 :(得分:-1)

尝试在ALTER TABLE之后添加“GO”语句。

这对我来说是新闻,但它说here批处理中的所有语句(GO之前的语句)都被编译成一个查询计划。)在SQL中没有GO,整个计划实际上是一个查询。

编辑:由于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