Dan*_*l F 4 t-sql transaction sql-server-2012 acid
我在 SO、MSDN 和其他各种来源上进行了一些搜索,发现了很多关于事务的问题,但似乎没有一个正是我正在处理的问题。
诚然,尽管我确实了解 ACID 和事务的概念,但我仍然是初级 DBA。我正在处理一个 SQL 脚本:
上述所有操作都是独立工作的,并且在(我认为是)单个事务中包含时,它们也会协同工作。我试图理解的是,当事务的另一部分失败时,事务的“部分”如何完成(并可能提交)。
为简洁起见,假设我有名为“ProcessReport”和“RetrieveReport”的工作程序。这两个过程中的动态 SQL 都以“SELECT”开头,然后从那里开始构建。测试用例:
CREATE TABLE ReportTable (FirstName VARCHAR(100), LastName VARCHAR(100), OrderId INT);
GO
CREATE PROCEDURE ProcessReport
AS
BEGIN
DECLARE @SQL VARCHAR(4000)
-- start creating dynamic sql
SET @SQL = 'SELECT FirstName, LastName, '
--create rest of dynamic SQL here...
INSERT INTO ReportTable (FirstName, LastName) EXEC(@SQL)
END
GO
CREATE PROCEDURE RetrieveReport
AS
BEGIN
DECLARE @SQL VARCHAR(4000)
-- start creating dynamic sql
SET @SQL = 'SELECT FirstName, LastName '
--create rest of dynamic SQL here...
SET @SQL = @SQL + ' FROM ReportTable '
EXEC (@SQL)
END
GO
Run Code Online (Sandbox Code Playgroud)
现在程序已经存在,这就是让我感到困惑的地方:当我故意为第一个输入错误名称时ALTER PROCEDURE
,我对第二个程序所做的编辑就完成了。以下是我写的交易的简短版本:
BEGIN TRANSACTION
GO
ALTER PROCEDURE ProcessReport2 -- this procedure does not exist, causing an error
AS
BEGIN
DECLARE @SQL VARCHAR(4000)
-- start creating dynamic sql
SET @SQL = 'SELECT FirstName, LastName, '
--create rest of dynamic SQL here...
INSERT INTO ReportTable(FirstName, LastName) EXEC(@SQL)
END
GO
ALTER PROCEDURE RetrieveReport
AS
BEGIN
DECLARE @SQL VARCHAR(4000)
-- start creating dynamic sql
SET @SQL = 'SELECT DISTINCT FirstName, LastName ' --DISTINCT added
--create rest of dynamic SQL here...
SET @SQL = @SQL + ' FROM ReportTable '
EXEC (@SQL)
END
GO
IF @@ERROR = 0
COMMIT TRANSACTION;
ELSE
ROLLBACK TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
运行上述语句时,出现以下错误:
消息 208,级别 16,状态 6,过程 ProcessReport2,第 2 行 对象名称“ProcessReport2”无效。
消息 3902,级别 16,状态 1,第 3 行 COMMIT TRANSACTION 请求没有相应的 BEGIN TRANSACTION。
关于 ACID 和事务,我的理解是,BEGIN TRANSACTION 和 COMMIT TRANSACTION 之间的一切要么完成,要么什么都不做。在 SSMS 中,当我在 RetrieveReport 过程中单击“修改”时,我现在看到以“SELECT DISTINCT ...”开头的动态 SQL
谁能告诉我我哪里出错了?是不是我的东西
IF @@ERROR = 0
COMMIT TRANSACTION;
ELSE
ROLLBACK TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
我的意图是“如果没有错误,就提交整个事情,如果有错误,什么都不做。”
我正在使用 SQL Server 2012 企业版。
任何帮助和/或见解将不胜感激。
@@ERROR
在每条语句后重置。您尝试更改第一个程序的错误不再可通过以下方式检测到@@ERROR
成功更改第二个程序后,。这是一个更简单的重现:
SELECT 1/0;
GO
SELECT @@ERROR; -- 8134
Run Code Online (Sandbox Code Playgroud)
但是,如果我在两者之间添加一个成功的声明:
SELECT 1/0;
GO
SELECT 1/1;
GO
SELECT @@ERROR; -- 0
Run Code Online (Sandbox Code Playgroud)
在您的情况下,您正在这样做:
ALTER dbo.p1 ... -- fails
GO
-- @@ERROR here would be <> 0, but you're not checking it here!
ALTER dbo.p2 ... -- succeeds
GO
-- You're checking @@ERROR here, but success is 0
Run Code Online (Sandbox Code Playgroud)
您当前的逻辑将所有内容回滚的唯一方法是最后一次 ALTER
失败。您需要@@ERROR
在每个 ALTER
. 这是一个例子。
USE tempdb;
GO
CREATE TABLE #x(err INT);
GO
BEGIN TRANSACTION;
GO
CREATE PROCEDURE dbo.foo -- fails
AS
SELECT foo FROM sys.objects;
GO
IF @@ERROR <> 0
INSERT #x(err) SELECT 1;
GO
CREATE PROCEDURE dbo.blat -- succeeds, but will get rolled back
AS
SELECT 1;
GO
IF @@ERROR <> 0
INSERT #x(err) SELECT 1;
GO
IF EXISTS (SELECT 1 FROM #x)
ROLLBACK TRANSACTION;
ELSE
COMMIT TRANSACTION;
DROP TABLE #x;
Run Code Online (Sandbox Code Playgroud)