Sql server 测试列存在、添加列和更新列
我正在尝试编写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
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语句之后的数据。只是要确保你记得清理后。