在后台运行大型查询MS SQL

在后台运行大型查询MS SQL,sql,sql-server,database,sql-server-2008,Sql,Sql Server,Database,Sql Server 2008,我正在使用MS SQL Server 2008 我有一个经常使用的表(数据总是在变化并插入到表中) 它现在包含约70百万行, 我正在尝试使用一个存储过程在表上运行一个简单的查询,这应该需要几天的时间 我需要表保持可用性,现在我执行了存储过程,过了一段时间,我尝试在表上执行的每个简单的select by identity查询都没有响应/运行太多时间,因此我中断了它 我该怎么办? 下面是我的存储过程的外观: SET NOCOUNT ON; update SOMETABLE set [some_co

我正在使用MS SQL Server 2008 我有一个经常使用的表(数据总是在变化并插入到表中) 它现在包含约70百万行, 我正在尝试使用一个存储过程在表上运行一个简单的查询,这应该需要几天的时间

我需要表保持可用性,现在我执行了存储过程,过了一段时间,我尝试在表上执行的每个简单的select by identity查询都没有响应/运行太多时间,因此我中断了它

我该怎么办? 下面是我的存储过程的外观:

 SET NOCOUNT ON;
update SOMETABLE
set
[some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE 
[some_col] = 243
即使我尝试在where子句中使用此选项(使用and逻辑…):

ID\u COL>57000000和ID\u COL<60000000和
它仍然不起作用


顺便说一句-SomeFunction执行一些简单的数学操作,并在另一个表中查找包含大约300k项的行,但从未更改过。从我的角度来看,您的服务器存在严重的性能问题。即使我们假设查询中没有任何记录

select some_col with (nolock) where id_col between 57000000 and 57001000
在内存中,从磁盘顺序读取几页不应该需要21秒(如果id_col上的聚集索引是自动标识,并且您没有做过像在索引定义中添加“desc”这样的蠢事,那么它就不应该被分割)

但是,如果您不能/不想解决这个问题,我的建议是在一次100-1000条记录这样的小程序包中进行更新(取决于查找函数消耗的时间)。一次更新/事务的时间不应超过30秒

您可以看到,每次更新都会对其修改的所有记录保持独占锁定,直到事务完成。如果不使用显式事务,则每个语句都在单个自动事务上下文中执行,因此在update语句完成时会释放锁

但是,您仍然可以通过这种方式遇到死锁,这取决于其他进程所做的工作。如果他们一次修改多条记录,或者即使他们收集并持有多行的读锁,也可能会出现死锁

为了避免死锁,update语句需要一次锁定它将修改的所有记录。实现这一点的方法是将单个update语句(只有少数行受id_col限制)放在可序列化事务中,如

IF @@TRANCOUNT > 0
  -- Error: You are in a transaction context already

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

-- Insert Loop here to work "x" through the id range
  BEGIN TRANSACTION
    UPDATE SOMETABLE
      SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
      WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
  COMMIT
-- Next loop

-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
  UPDATE SOMETABLE
    SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
    WHERE [some_col] = 243 AND id_col >= x
COMMIT
对于每个更新,这将对给定记录(但仅限于它们,因为您通过聚集索引键限制更新)使用更新/独占密钥范围锁。它将等待相同记录上的任何其他更新完成,然后获取其锁(导致所有其他事务被阻塞,但仍然仅限于给定记录),然后更新记录并释放锁


最后一个额外语句很重要,因为它将把密钥范围锁定到“无穷大”,从而在update语句运行时防止在范围末尾插入任何内容。

您使用的是什么事务级别?某些列上有索引吗?ID列上有什么类型的索引?ID列是一个主标识(增长整数)。。。没有索引。readcommit事务级别…我认为真正的问题在于,您的存储过程预计需要几天才能执行。您确定无法对其进行优化吗?如果执行“选择id在57000000和57000100之间的(nolock)某些列”,需要多长时间?如果花费的时间超过几分钟,请中止查询并说“超过几分钟”:-)select query花费了0秒…很抱歉,可能存在误解-在57000000和5701000之间运行存储过程花费了21秒。。如您所见,选择100个项目花费了0秒。。。不管怎样,你的回答似乎是合理的,我会尝试一下并发回。谢谢你的帮助!是的,这就是为什么我在答案中引用了1000项查询:-)。我认为21秒通过一个带聚集索引的主键选择1000个整数(nolock)太多了。由于(nolock),21秒不能来自另一个阻塞锁,如果让它直接在服务器上运行,也不会是网络问题。这只是移动到第一个索引项并从以下记录中获取1000个整数的原始性能。我会考虑一个正常的服务器,它应该在1秒内,即使是一个很长的磁盘队列。无论如何,只需实验一下窗口大小就可以得到每个更新的合理时间。查询运行大约14个小时,它看起来很好,所以非常感谢。
IF @@TRANCOUNT > 0
  -- Error: You are in a transaction context already

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

-- Insert Loop here to work "x" through the id range
  BEGIN TRANSACTION
    UPDATE SOMETABLE
      SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
      WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
  COMMIT
-- Next loop

-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
  UPDATE SOMETABLE
    SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
    WHERE [some_col] = 243 AND id_col >= x
COMMIT