T-SQL事务-此开始/提交实现是否足够?
我正在创建一个SQL Server批处理文件,以创建新的数据库对象、将数据插入表等。如果其中一个操作失败,那么我不希望提交任何其他操作 以下包装是否足以完成我要做的事情T-SQL事务-此开始/提交实现是否足够?,sql,sql-server,tsql,transactions,acid,Sql,Sql Server,Tsql,Transactions,Acid,我正在创建一个SQL Server批处理文件,以创建新的数据库对象、将数据插入表等。如果其中一个操作失败,那么我不希望提交任何其他操作 以下包装是否足以完成我要做的事情 BEGIN TRANSACTION --script #1 - create Table1 --script #2 - create Sproc1 --script #3 - insert data load into Table1 COMMIT 因此,如果SS在上面定义的事务中嵌套的任何脚本中遇到错误,则不会提交任何SQ
BEGIN TRANSACTION
--script #1 - create Table1
--script #2 - create Sproc1
--script #3 - insert data load into Table1
COMMIT
因此,如果SS在上面定义的事务中嵌套的任何脚本中遇到错误,则不会提交任何SQL DDL或数据加载,对吗
我假设这里不需要显式回滚。是否存在需要显式包含回滚语句的场景?请参阅。某些错误只会终止正在运行的语句,并在下一条语句上继续执行。那太糟糕了
您可以通过将XACT_ABORT设置为ON,确保脚本中没有设置它,并在出现错误时回滚或关闭连接来实现这一点。例如:
SET XACT_ABORT ON
BEGIN TRANSACTION
--script #1 - create Table1
--script #2 - create Sproc1
--script #3 - insert data load into Table1
COMMIT
但正如@Larnu所指出的,最好使用并显式回滚事务
请注意,某些DDL语句必须在自己的批处理中,或者是批处理中的第一条语句。因此,您必须在脚本中使用动态SQL或多个批处理。并尝试..CATCH只能在单个批处理中工作。显示这一点的最简单方法是使用一些示例代码。首先,让我们进行如下设置:
BEGIN TRANSACTION;
DECLARE @SQL nvarchar(MAX);
CREATE TABLE dbo.Table1 (ID int);
--Needs to be dynamic, as CREATE PROC must be in its own batch
SET @SQL = N'
CREATE PROC dbo.Proc1 @i int AS
SELECT ID
FROM dbo.Table1
WHERE ID = @i;';
EXEC sp_executesql @SQL;
--Works fine
INSERT INTO dbo.Table1 (ID)
SELECT 1;
--Fails
INSERT INTO dbo.Table1 (ID)
SELECT 0/0;
COMMIT;
现在,如果我们尝试以下方法,则会出现错误:
SELECT *
FROM dbo.Table1;
您会注意到这是有效的,并返回数据。以下语句也不会返回任何错误:
EXEC dbo.Proc1 @i = 1;
GO
--Both work
DROP PROC dbo.Proc1;
DROP TABLE dbo.Table1;
其他语句都没有在除法为零的错误上回滚,这不是您想要的。正如我在评论中所说,您需要使用TRY…CATCH
;因此,您的批次看起来像:
BEGIN TRY
BEGIN TRANSACTION Migration;
DECLARE @SQL nvarchar(MAX);
CREATE TABLE dbo.Table1 (ID int);
--Needs to be dynamic, as CREATE PROC must be in its own batch
SET @SQL = N'
CREATE PROC dbo.Proc1 @i int AS
SELECT ID
FROM dbo.Table1
WHERE ID = @i;';
EXEC sp_executesql @SQL;
--Works fine
INSERT INTO dbo.Table1 (ID)
SELECT 1;
--Fails
INSERT INTO dbo.Table1 (ID)
SELECT 0/0;
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION Migration;
END CATCH
现在,如果我们尝试上述任何一种说法,它们都将失败:
--Doesn't work
SELECT *
FROM dbo.Table1;
GO
--Doesn't work
EXEC dbo.Proc1 @i = 1;
GO
--Deosn't work
DROP PROC dbo.Proc1;
DROP TABLE dbo.Table1;
GO
正如我在上面的评论中所说,这意味着
SELECT
、EXEC
和DROP
语句都会失败,因为对象不存在。您使用的是InnoDb还是MySQL?MySQL将创建表作为一个事务处理。因此,即使您将其放入外部事务包装器中,它也无法撤消。我使用的是SQL Server 2016,您需要使用开始尝试。。。CATCH
带有显式的回滚功能。您能在事务中创建存储过程吗?@Larnu-如果在“开始事务”之后但在“提交”之前发生错误,最新版本的sql server不会在后台简单地回滚当前事务吗?这是有关XACT\U ABORT的有趣信息。我认为大多数非sql专家都会假设,当嵌套sql语句中发生错误时,标准的“BeginTransaction/COMMIT”将实现完全回滚。你能想象一下现实世界中有人需要在不启用XACT_ABORT的情况下运行“BEGIN TRANSACTION/COMMIT”吗?我不确定下面的语句是什么意思:“注意,一些DDL语句必须在它们自己的批中,或者是批中的第一个语句。”请详细说明?