Azure SQL Server:确保SQL命令按顺序执行而非并行的方法
问题:Azure SQL数据库脚本执行时因列未创建完成报错
我在Microsoft Azure上运行着一个数据库,需在其中执行一段脚本。当前问题是脚本仅在SQL命令逐个执行时才能正常工作,但实际执行时多个命令会触发编译错误,例如SQL尝试在列创建完成前就更新该列,报错信息如下:
Failed to execute query. Error: Invalid column name 'table_1_id'我需要确保ADD命令先执行完成后再执行UPDATE命令,如何让脚本中所有命令依次执行而非出现编译错误?以下是我的代码:
BEGIN TRY BEGIN TRANSACTION ALTER TABLE table_1 ADD table_1_id BIGINT; UPDATE table_1 SET table_1_id = table_1_id2; ALTER TABLE table2 DROP CONSTRAINT constraint1; ALTER TABLE table_1 DROP CONSTRAINT PK_1; ALTER TABLE table_1 DROP COLUMN table_1_id2; ALTER TABLE table_1 ADD table_1_id2 BIGINT IDENTITY PRIMARY KEY; COMMIT TRAN -- Transaction Success! END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRAN --RollBack in case of Error -- <EDIT>: From SQL2008 on, you must raise error messages as follows: DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); -- </EDIT> END CATCH
原因说明
SQL Server(包括Azure SQL)会在执行整个批处理前对所有语句做编译检查。你的脚本里,UPDATE语句引用的table_1_id列是在同一个批处理里用ALTER TABLE新增的,编译阶段该列还不存在,所以直接抛出错误,根本不会执行到创建列的步骤,并非命令真的并行运行。
解决方案
解决核心是让依赖新增列的语句避开编译阶段的存在性检查,有两种可靠方式:
方式1:使用动态SQL执行更新语句
动态SQL会在运行时才编译,此时列已经被创建,不会触发编译错误:
BEGIN TRY BEGIN TRANSACTION ALTER TABLE table_1 ADD table_1_id BIGINT; -- 用动态SQL执行更新,绕开编译时的列存在性检查 EXEC('UPDATE table_1 SET table_1_id = table_1_id2'); ALTER TABLE table2 DROP CONSTRAINT constraint1; ALTER TABLE table_1 DROP CONSTRAINT PK_1; ALTER TABLE table_1 DROP COLUMN table_1_id2; ALTER TABLE table_1 ADD table_1_id2 BIGINT IDENTITY PRIMARY KEY; COMMIT TRAN -- Transaction Success! END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRAN --RollBack in case of Error -- <EDIT>: From SQL2008 on, you must raise error messages as follows: DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); -- </EDIT> END CATCH
方式2:用GO分隔独立批处理
GO是SQL客户端工具的批处理分隔符,每个批处理会被数据库逐次执行,前一个批处理完成后才会执行下一个。注意事务不能跨GO,所以需要调整结构确保事务完整性:
BEGIN TRY BEGIN TRANSACTION TableUpdateTrans ALTER TABLE table_1 ADD table_1_id BIGINT; GO -- 继续之前的事务 UPDATE table_1 SET table_1_id = table_1_id2; ALTER TABLE table2 DROP CONSTRAINT constraint1; ALTER TABLE table_1 DROP CONSTRAINT PK_1; ALTER TABLE table_1 DROP COLUMN table_1_id2; ALTER TABLE table_1 ADD table_1_id2 BIGINT IDENTITY PRIMARY KEY; COMMIT TRANSACTION TableUpdateTrans -- Transaction Success! END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION TableUpdateTrans --RollBack in case of Error -- <EDIT>: From SQL2008 on, you must raise error messages as follows: DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); -- </EDIT> END CATCH
注意:部分客户端工具可能在
GO后无法保留事务上下文,因此方式1(动态SQL)在Azure SQL环境中更稳定可靠。
内容的提问来源于stack exchange,提问作者coder
相关产品推荐
相关产品推荐

