将列添加到表,然后在事务内更新它

Gui*_*mez 65 sql t-sql sql-server transactions alter-table

我正在创建一个将在MS SQL服务器中运行的脚本.此脚本将运行多个语句,并且需要是事务性的,如果其中一个语句失败,则停止整体执行并回滚任何更改.

在发出ALTER TABLE语句以向表中添加列然后更新新添加的列时,我在创建此事务模型时遇到问题.为了立即访问新添加的列,我使用GO命令执行ALTER TABLE语句,然后调用我的UPDATE语句.我面临的问题是我无法在IF语句中发出GO命令.IF语句在我的事务模型中很重要.这是我尝试运行的脚本的示例代码.另请注意,发出GO命令会丢弃@errorCode变量,并且需要在使用之前在代码中声明(这不在下面的代码中).

BEGIN TRANSACTION

DECLARE @errorCode INT
SET @errorCode = @@ERROR

-- **********************************
-- * Settings
-- **********************************
IF @errorCode = 0
BEGIN
 BEGIN TRY
  ALTER TABLE Color ADD [CodeID] [uniqueidentifier] NOT NULL DEFAULT ('{00000000-0000-0000-0000-000000000000}')
  GO
 END TRY
 BEGIN CATCH
  SET @errorCode = @@ERROR
 END CATCH
END

IF @errorCode = 0
BEGIN
 BEGIN TRY
  UPDATE Color
  SET CodeID= 'B6D266DC-B305-4153-A7AB-9109962255FC'
  WHERE [Name] = 'Red'
 END TRY
 BEGIN CATCH
  SET @errorCode = @@ERROR
 END CATCH
END

-- **********************************
-- * Check @errorCode to issue a COMMIT or a ROLLBACK
-- **********************************
IF @errorCode = 0
BEGIN
 COMMIT
 PRINT 'Success'
END
ELSE 
BEGIN
 ROLLBACK
 PRINT 'Failure'
END
Run Code Online (Sandbox Code Playgroud)

所以我想知道的是如何解决这个问题,发出ALTER TABLE语句来添加一个列,然后更新该列,所有这些都在作为事务单元执行的脚本中.

Rem*_*anu 43

GO不是T-SQL命令.是批处理分隔符.客户端工具(SSM,sqlcmd,osql等)使用它来有效地剪切每个GO上的文件,并将各个批次发送到服务器.所以显然你不能在IF中使用GO,也不能期望变量跨越批次的范围.

此外,如果不检查XACT_STATE()以确保事务没有注定,您就无法捕获异常.

使用GUID进行ID始终至少是可疑的.

使用NOT NULL约束并提供默认的'guid' '{00000000-0000-0000-0000-000000000000}'也不正确.

更新:

  • 将ALTER和UPDATE分成两批.
  • 使用sqlcmd扩展可以在出错时破坏脚本.当sqlcmd模式打开时,SSMS支持这一点,sqlcmd,并且在客户端库中也很容易支持它:dbutilsqlcmd.
  • 用于XACT_ABORT强制错误中断批处理.这经常用于维护脚本(架构更改).存储过程和应用程序逻辑脚本通常使用TRY-CATCH块,但要小心谨慎:异常处理和嵌套事务.

示例脚本:

:on error exit

set xact_abort on;
go

begin transaction;
go

if columnproperty(object_id('Code'), 'ColorId', 'AllowsNull') is null
begin
    alter table Code add ColorId uniqueidentifier null;
end
go

update Code 
  set ColorId = '...'
  where ...
go

commit;
go
Run Code Online (Sandbox Code Playgroud)

只有成功的脚本才能到达COMMIT.任何错误都将中止脚本和回滚.

我曾经COLUMNPROPERTY检查过列存在,你可以使用你喜欢的任何方法(例如查找sys.columns).


Mar*_*wul 23

与Remus的注释正交,您可以做的是在sp_executesql中执行更新.

ALTER TABLE [Table] ADD [Xyz] NVARCHAR(256);

DECLARE @sql NVARCHAR(2048) = 'UPDATE [Table] SET [Xyz] = ''abcd'';';
EXEC sys.sp_executesql @query = @sql;
Run Code Online (Sandbox Code Playgroud)

创建升级脚本时我们需要这样做.通常我们只使用GO,但有必要有条件地做事.


gbn*_*gbn 18

我几乎同意Remus,但您可以使用SET XACT_ABORT ON和XACT_STATE执行此操作

基本上

  • SET XACT_ABORT ON将在出错和ROLLBACK时中止每个批处理
  • 每批由GO分隔
  • 执行错误时会跳转到下一批
  • 使用XACT_STATE()将测试事务是否仍然有效

像Red Gate SQL Compare这样的工具使用这种技术

就像是:

SET XACT_ABORT ON
GO
BEGIN TRANSACTION
GO

IF COLUMNPROPERTY(OBJECT_ID('Color'), 'CodeID', ColumnId) IS NULL
   ALTER TABLE Color ADD CodeID [uniqueidentifier] NULL
GO

IF XACT_STATE() = 1
  UPDATE Color
  SET CodeID= 'B6D266DC-B305-4153-A7AB-9109962255FC'
  WHERE [Name] = 'Red'
GO

IF XACT_STATE() = 1
 COMMIT TRAN
--else would be rolled back
Run Code Online (Sandbox Code Playgroud)

我也删除了默认值.对于GUID值,没有值= NULL.它的意思是独一无二的:不要试图将每一行设置为全零,因为它会以泪水结束......