8

作为报告模块的一部分,我正在执行几个长时间运行的 SQL 查询。这些查询是在运行时动态构建的。根据用户的输入,它们可能是单语句或多语句,具有一个或多个参数并在一个或多个数据库表上进行操作——换句话说,它们的形式不能轻易预料。

目前,我只是在普通SqlConnection的 IE上执行这些语句

using (SqlConnection cn = new SqlConnection(ConnectionString)) {
    cn.Open();
    // command 1
    // command 2
    // ...
    // command N
}

因为这些查询(实际上是批量查询)可能需要一段时间才能执行,所以我担心表上的锁会阻碍其他用户的读/写。如果这些报告的数据在批处理执行期间发生变化,这不是问题;报表查询不应优先于这些表上的其他操作,也不应锁定它们。

对于大多数涉及修改数据的长时间运行/多语句操作,我会使用事务。此处的区别在于这些报表查询不会修改任何数据。SqlTransaction为了控制它们的隔离级别,我是否正确包装这些报告查询?

IE:

using (SqlConnection cn = new SqlConnection(ConnectionString)) {
    cn.Open();

    using (SqlTransaction tr = cn.BeginTransaction(IsolationLevel.ReadUncommitted)) {
        // command 1
        // command 2
        // ...
        // command N

        tr.Commit();
    }
}

这会达到我想要的结果吗?即使没有修改任何数据,提交事务是否正确?还有另一种方法吗?

4

2 回答 2

5

另一种方法可能是针对连接发出:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

它实现了相同的意图,而不会弄乱事务。或者,您可以WITH(NOLOCK)在查询中使用表上的提示,其优点是根本不更改连接。

重要的是,请注意(不寻常地):无论它被更改(事务、事务范围、显式SET等),在从池中获取同一底层连接时,隔离级别不会在使用之间重置。这意味着如果您的代码更改了隔离级别(直接或间接),那么您的代码都不知道新连接的隔离级别是什么:

using(var conn = new SqlConnection(connectionString)) {
    conn.Open();
    // isolation level here could be **ANYTHING**; it could be the default
    // if it is a brand new connection, or could be whatever the last
    // connection was when it finished
}

这使得WITH(NOLOCK)相当诱人。

于 2012-09-24T07:13:31.200 回答
1

我同意 Marc 的观点,但您也可以在受影响的表上使用 NOLOCK 查询提示。这将使您能够按表级别在表上控制它。

在不使用共享锁的情况下运行任何查询的问题在于,您让自己对“非确定性”结果持开放态度,并且不应根据这些数据做出业务决策。

更好的方法可能是调查 SNAPSHOT 或 READ_COMMITED_SNAPSHOT 隔离级别。这些为您提供了针对事务异常的保护,而无需锁定。权衡是它们增加了针对 TempDB 的 IO。这些级别中的任何一个都可以应用于 Marc 建议的会话或我建议的表格。

希望这可以帮助

于 2012-09-24T07:20:01.617 回答