我正在创建一个在事务中运行多个sql脚本的数据库更新机制,如果所有这些脚本都成功,则会提交更新。
我面临的问题是,在每个脚本中都可能存在正在处理的错误,并且实际上并未将其视为错误。
示例:
脚本必须创建一个表并向表中插入一行,但该表已经存在。没关系,脚本应继续并尝试插入行,如果行存在 - 它也没关系,它应该完成没有错误并转移到下一个脚本。另一方面 - 如果出现意外错误(例如表存在,但列数较少,因此插入失败),则应抛出错误并回滚事务。
由于SQL Server在每个错误上回滚我的事务(无论是否处理),我都无法正确执行更新。
示例脚本如下所示:
USE [Database]
BEGIN TRY
-- CREATING TABLE
CREATE TABLE [dbo].[Users](
[UserId] [int] UNIQUE NOT NULL,
[UserFullName] [nvarchar](100) NOT NULL,
[UserName] [nvarchar](100) NOT NULL,
[UserShortName] [nvarchar](50) NOT NULL,
[UserLogin] [varchar](50) NOT NULL
)
PRINT 'Created table'
END TRY
BEGIN CATCH
IF(ERROR_NUMBER() = 2714)
BEGIN
PRINT 'Table exists, proceeding to insert'
END
ELSE
THROW
END CATCH
-- INSERTING USER
BEGIN TRY
INSERT INTO [dbo].[Users](
[UserId]
,[UserFullName]
,[UserName]
,[UserShortName]
,[UserLogin]
) VALUES (
1,'System Administrator','Admin','SA', 'SA'
)
PRINT 'Inserted user'
END TRY
BEGIN CATCH
IF(ERROR_NUMBER() = 2627)
BEGIN
PRINT 'User exists - nothing to insert'
END
ELSE
THROW
END CATCH
GO
PRINT '-- FINISHED add_users_table.sql'
(该脚本只是一个例子,因此它可能不是100%正确)
如果存在表或行,则事务应继续,但如果抛出不同的错误,则应该回滚。
有办法做到这一点吗?
谢谢!
编辑:
我忘记了机制的关键部分。我从C#运行这些脚本(它更像是伪代码而不是实际代码,我也知道SqlCommand中的GO命令不起作用,但它与问题无关):
SqlTransaction transaction =
connection.BeginTransaction(IsolationLevel.ReadUncommited);
SqlCommand command = new SqlCommand("");
command.Connection = connection;
foreach(string script in scripts)
{
command.CommandText = script;
try
{
command.ExecuteNonQuery();
}
catch()
{
transaction.Rollback();
}
}
transaction.Commit();
答案 0 :(得分:3)
在运行插入之前,仅检查对象是否存在或用户是否存在似乎更简单,例如:
if not exists (select 1 from sysobjects where name=N'Users' and xtype='U')
begin;
CREATE TABLE [dbo].[Users](
[UserId] [int] UNIQUE NOT NULL,
[UserFullName] [nvarchar](100) NOT NULL,
[UserName] [nvarchar](100) NOT NULL,
[UserShortName] [nvarchar](50) NOT NULL,
[UserLogin] [varchar](50) NOT NULL
);
PRINT 'Created table'
end;
if not exists (select 1 from dbo.users where userlogin = 'SA')
begin;
INSERT INTO [dbo].[Users](
[UserId]
,[UserFullName]
,[UserName]
,[UserShortName]
,[UserLogin]
) VALUES (
1,'System Administrator','Admin','SA', 'SA'
)
PRINT 'Inserted user'
end;
答案 1 :(得分:2)
首先,我同意SqlZim建议的解决方案可能是要走的路。
我想从Microsoft's docs添加一些信息,我认为很好地解释了你不能这样做:
如果TRY块中生成的错误导致当前事务的状态无效,则该事务被分类为不可提交的事务。 [...]。不可提交的事务只能执行读操作或ROLLBACK TRANSACTION。事务不能执行任何会生成写操作或COMMIT TRANSACTION的Transact-SQL语句。
换句话说;当您的脚本失败时,它会导致事务进入不可提交的状态。必须回滚事务 - 它不能被提交。由于脚本中的所有语句都在同一事务中执行,因此必须全部回滚它们。