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