我正在做一些数据库架构重组。
我的脚本大致如下:
BEGIN TRAN LabelledTransaction
--Remove FKs
ALTER TABLE myOtherTable1 DROP CONSTRAINT <constraintStuff>
ALTER TABLE myOtherTable2 DROP CONSTRAINT <constraintStuff>
--Remove PK
ALTER TABLE myTable DROP CONSTRAINT PK_for_myTable
--Add replacement id column with new type and IDENTITY
ALTER TABLE myTable ADD id_new int Identity(1, 1) NOT NULL
GO
ALTER TABLE myTable ADD CONSTRAINT PK_for_myTable PRIMARY KEY CLUSTERED (id_new)
GO
SELECT * FROM myTable
--Change referencing table types
ALTER TABLE myOtherTable1 ALTER COLUMN col_id int NULL
ALTER TABLE myOtherTable2 ALTER COLUMN col_id int NOT NULL
--Change referencing table values
UPDATE myOtherTable1 SET consignment_id = Target.id_new FROM myOtherTable1 AS Source JOIN <on key table>
UPDATE myOtherTable2 SET consignment_id = Target.id_new FROM myOtherTable2 AS Source JOIN <on key table>
--Replace old column with new column
ALTER TABLE myTable DROP COLUMN col_id
GO
EXEC sp_rename 'myTable.id_new', 'col_id', 'Column'
GO
--Reinstate any OTHER PKs disabled
ALTER TABLE myTable ADD CONSTRAINT <PK defn>
--Reinstate FKs
ALTER TABLE myOtherTable1 WITH CHECK ADD CONSTRAINT <constraintStuff>
ALTER TABLE myOtherTable2 WITH CHECK ADD CONSTRAINT <constraintStuff>
SELECT * FROM myTable
-- Reload out-of-date views
EXEC sp_refreshview 'someView'
-- Remove obsolete sequence
DROP SEQUENCE mySeq
ROLLBACK TRAN LabelledTransaction
显然,所有这些都有所修改,但细节并不是重要的事情。
当然,在核心更改之前找到所有需要关闭/编辑的东西是很困难的(即使有一些元查询可以帮助我),所以我并不总是第一次得到正确的脚本。
但我放入ROLLBACK以确保失败的尝试使DB保持不变。
但我实际看到的是,如果TRAN中存在错误,则不会发生ROLLBACK。我想我得到的错误是“没有匹配TRAN回滚”?
我的第一直觉是关于GO
陈述,但是https://stackoverflow.com/a/11121382/1662268建议标记TRAN应该已经修复了吗?
发生了什么?如果有错误,为什么不能正确回滚更改。
如何以这样的方式编写和测试这些脚本:如果脚本第一次不完美,我不必手动还原任何部分更改?
修改 基于第一个答案的附加评论。
如果链接的答案不适用于此查询,您是否可以扩展其原因,以及为什么它与答案中的示例不同?
我不能(或者说,我相信我不能)删除GO
,因为上面的脚本需要GO
s才能编译。如果我删除GO
s,那么后来依赖于新添加/重命名的列的语句不会编译。并且查询无法运行。
有没有办法解决这个问题,删除GO
?
答案 0 :(得分:4)
如果您有任何错误会自动导致事务回滚,那么事务将作为当前批次的一部分回滚。
然后,控制将返回到客户端工具,然后将下一批次发送到服务器,下一批(以及后续批处理)将不会包装在任何事务中。
最后,当执行最后一批尝试运行回滚的批处理时,您将收到收到的错误消息。
因此,当不受事务保护时,您需要保护每个批次的运行。
一种方法是插入旧的GOTO
:
GO
IF @@TRANCOUNT=0 GOTO NBATCH
...Rest of Code
NBATCH:
GO
或SET FMTONLY
:
GO
IF @@TRANCOUNT=0 BEGIN
SET FMTONLY ON
END
...Rest of Code
GO
当然,这不会解决所有问题 - 某些语句需要是批处理中的第一个或唯一的语句。要解决这些问题,我们必须将上述技术之一与某种形式的EXEC
结合使用:
GO
IF @@TRANCOUNT=0 BEGIN
SET FMTONLY ON
END
EXEC sp_executesql N'/*Code that needs to be in its own batch*/'
GO
(如果一批代码依赖于先前批处理已执行的工作而引入 new 数据库对象(表,列等),则还必须使用此技术,因为如果之前的那个批量永不执行,新对象将不存在)
我刚刚发现sqlcmd tool存在-b
选项。以下脚本在通过SSMS运行时会生成两个错误:
begin transaction
go
set xact_abort on
go
create table T(ID int not null,constraint CK_ID check (ID=4))
go
insert into T(ID) values (3)
go
rollback
错误:
Msg 547, Level 16, State 0, Line 7
The INSERT statement conflicted with the CHECK constraint "CK_ID". The conflict occurred in database "TestDB", table "dbo.T", column 'ID'.
Msg 3903, Level 16, State 1, Line 9
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
但是,相同的脚本保存为Abortable.sql
并使用以下命令行运行:
sqlcmd -b -E -i Abortable.sql -S .\SQL2014 -d TestDB
生成单个错误:
Msg 547, Level 16, State 1, Server .\SQL2014, Line 1
The INSERT statement conflicted with the CHECK constraint "CK_ID". The conflict
occurred in database "TestDB", table "dbo.T", column 'ID'.
因此,看起来从命令行运行脚本并使用-b
选项可能是另一种方法。我刚刚搜索了SSMS选项/属性,看看我是否能找到与-b
相当的东西但是我找不到它。
答案 1 :(得分:-2)
删除完成交易的“GO”
答案 2 :(得分:-2)
如果完成只有ROLLBACK - 只需使用TRY / CATCH:
BEGIN TRANSACTION;
BEGIN TRY
--Remove FKs
ALTER TABLE myOtherTable1 DROP CONSTRAINT <constraintStuff>
ALTER TABLE myOtherTable2 DROP CONSTRAINT <constraintStuff>
--Remove PK
ALTER TABLE myTable DROP CONSTRAINT PK_for_myTable
--Add replacement id column with new type and IDENTITY
ALTER TABLE myTable ADD id_new int Identity(1, 1) NOT NULL
ALTER TABLE myTable ADD CONSTRAINT PK_for_myTable PRIMARY KEY CLUSTERED (id_new)
SELECT * FROM myTable
--Change referencing table types
ALTER TABLE myOtherTable1 ALTER COLUMN col_id int NULL
ALTER TABLE myOtherTable2 ALTER COLUMN col_id int NOT NULL
--Change referencing table values
UPDATE myOtherTable1 SET consignment_id = Target.id_new FROM myOtherTable1 AS Source JOIN <on key table>
UPDATE myOtherTable2 SET consignment_id = Target.id_new FROM myOtherTable2 AS Source JOIN <on key table>
--Replace old column with new column
ALTER TABLE myTable DROP COLUMN col_id
EXEC sp_rename 'myTable.id_new', 'col_id', 'Column'
--Reinstate any OTHER PKs disabled
ALTER TABLE myTable ADD CONSTRAINT <PK defn>
--Reinstate FKs
ALTER TABLE myOtherTable1 WITH CHECK ADD CONSTRAINT <constraintStuff>
ALTER TABLE myOtherTable2 WITH CHECK ADD CONSTRAINT <constraintStuff>
SELECT * FROM myTable
-- Reload out-of-date views
EXEC sp_refreshview 'someView'
-- Remove obsolete sequence
DROP SEQUENCE mySeq
ROLLBACK TRANSACTION
END TRY
BEGIN CATCH
print 'Error caught'
select ERROR_NUMBER() AS ErrorNumber, ERROR_MESSAGE() AS ErrorMessage;
END CATCH;