不间断地执行一个SQL块

时间:2016-06-28 18:45:38

标签: c# sql sql-server ado.net

假设我在Web应用程序中使用来自C#的ExecuteNonQuery(DbCommand)执行以下SQL语句

DECLARE @InsertedProductID INT -- this is passed as a parameter
DECLARE @GroupID INT -- this is passed as a parameter
DECLARE @total INT
SET @total = (SELECT COUNT (*) FROM Products WHERE GroupID = @GroupID)
UPDATE Products SET ProdName = 'Prod_'+ CAST(@total as varchar(15)) 
WHERE ProductID = @InsertedProductID

我的问题是我想确保整个块在一个块上执行。我的目标是始终每组ProdName唯一。如果我按照原样保留所有内容,如果在insert和执行@total之间发生UPDATE,我很可能会获得重复的产品名称。有没有办法确保整个SQL块一次执行而不会中断。 execsp_executesql会实现这一目标吗?我的最后一招是将lock放在ExecuteNonQuery(DbCommand)附近但我不喜欢它,因为它会造成瓶颈。我不认为使用sql事务在这里是有用的,因为我并不担心命令的完整性,我更担心命令的并行性。

4 个答案:

答案 0 :(得分:3)

通常任何DML语句(UPDATE/INSERT/DELETE)都会在特定表上放置一个锁(行级/表级),但是如果您想明确保证您的操作不会干扰其他执行语句那么您应考虑将整个SQL块放在事务块中

Begin transaction
begin try
DECLARE @InsertedProductID INT -- this is passed as a parameter
DECLARE @GroupID INT -- this is passed as a parameter
DECLARE @total INT
SET @total = (SELECT COUNT (*) FROM Products WHERE GroupID = @GroupID)
UPDATE Products SET ProdName = 'Prod_'+ CAST(@total as varchar(15)) WHERE ProductID = @InsertedProductID

commit; // commits the transaction
end try
begin catch
rollback; //Rolls back the transaction
end catch
end 

您还应考虑将Transaction Isolation Level改为READ COMMITTED以避免dirty reads。此外,显然您应该将整个逻辑包装在stored procedure中,而不是将它们作为adhoc SQL

执行

答案 1 :(得分:3)

如果您可以控制SqlConnection对象的创建,请考虑使用Transactions和相应的IsolationLevel依赖数据库锁。例如,如果在提交发生之前单独的事务触及数据,则使用快照将导致第二个事务提交失败。

类似的东西:

var c = new SqlConnection(...);
var tran1 = c.BeginTransaction(IsolationLevel.Snapshot);
var tran2 = c.BeginTransaction(IsolationLevel.Snapshot);
DoStuff(c, tran1);//Touch some database data
tran1.Commit();
DoStuff(c, tran2);//Change the same data
tran2.Commit();//Error!

答案 2 :(得分:1)

不太确定你不能这么做

UPDATE Products 
SET ProdName = 'Prod_'+ CAST((SELECT COUNT (*) 
                              FROM Products 
                              WHERE GroupID = @GroupID) as varchar(15)) 
WHERE ProductID = @InsertedProductID

但对我来说这是一个奇怪的更新

答案 3 :(得分:1)

使用交易是正确的方法。除了其他答案,您还可以使用TransactionScopeTransactionScope隐式地将连接和SQL命令注册到事务中。如果由于TransactionScope在使用块中而存在问题,则会自动发生回滚。

示例:

        try
        {
            using (var scope = new TransactionScope())
            {
                using (var conn = new SqlConnection("your connection string"))
                {
                    conn.Open();
                    var cmd = new SqlCommand("your SQL here", conn);
                    cmd.ExecuteNonQuery();
                }

                scope.Complete();
            }
        }
        catch (TransactionAbortedException ex)
        {

        }
        catch (ApplicationException ex)
        {

        }