我正在执行几个长时间运行的SQL查询,作为报告模块的一部分。这些查询是在运行时动态构造的。根据用户的输入,它们可以是单个或多个语句,具有一个或多个参数并在一个或多个数据库表上运行 - 换句话说,它们的形式不容易预期。
目前,我只是在普通SqlConnection
上执行这些陈述,即
using (SqlConnection cn = new SqlConnection(ConnectionString)) {
cn.Open();
// command 1
// command 2
// ...
// command N
}
因为这些查询(实际上是查询批处理)可能需要一段时间才能执行,所以我担心会阻塞其他用户的读/写表。如果这些报告的数据在批处理执行期间发生变化,则不会出现问题。报告查询永远不应该优先于那些表上的其他操作,也不应该锁定它们。
对于涉及修改数据的大多数长时间运行/多语句操作,我会使用事务。这里的区别在于这些报告查询不会修改任何数据。我是否可以正确地将这些报告查询包装在SqlTransaction
中以控制其隔离级别?
即:
using (SqlConnection cn = new SqlConnection(ConnectionString)) {
cn.Open();
using (SqlTransaction tr = cn.BeginTransaction(IsolationLevel.ReadUncommitted)) {
// command 1
// command 2
// ...
// command N
tr.Commit();
}
}
这会达到我想要的结果吗?提交事务是否正确,即使没有修改数据?还有另一种方法吗?
答案 0 :(得分: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)
非常诱人。
答案 1 :(得分:1)
我同意Marc,但您也可以在受影响的表上使用NOLOCK查询提示。这将使您能够按表级别在表上控制它。
在不使用共享锁的情况下运行任何查询的问题是,您可能会对“非确定性”结果持开放态度,并且不应对此数据做出业务决策。
更好的方法可能是调查SNAPSHOT或READ_COMMITED_SNAPSHOT隔离级别。这些可以帮助您防止事务性失真而无需锁定。权衡的是他们增加了对抗TempDB的IO。这些级别中的任何一个都可以应用于Marc建议的会话或我建议的表格。
希望这有帮助