在单个语句与批处理中执行SQL更新

时间:2013-02-03 15:32:01

标签: sql performance stored-procedures

我正在使用大型数据库,需要有关如何优化我的选择/更新的建议。这是一个例子:

create table Book (
   BookID int,
   Description  nvarchar(max)
)
-- 8 million rows

create table #BookUpdates (
   BookID int,
   Description  nvarchar(max)
)
-- 2 million rows

我们假设有800万册书籍,我必须更新其中200万册。

问题:运行这些更新的时间很长。它偶尔会导致阻止那些也试图从数据库运行语句的用户。我想出了一个解决方案,但想知道那里是否有更好的解决方案。我必须准备像这样的一次性随机更新(无论出于何种原因)

-- normal update
update b set b.Description = bu.Description
from Book b
join #BookUpdates bu
   on bu.BookID = b.BookID

-- batch update
while (@BookID < @MaxBookID)
begin
   update b set b.Description = bu.Description
   from Book b
   join #BookUpdates bu
      on bu.BookID = b.BookID
   where bu.BookID >= @BookID
      and bu.BookID < @BookID + 5000

   set @BookID = @BookID + 5000
end

第二次更新的速度要快得多。我喜欢这个解决方案,因为我可以打印状态更新给自己剩下多长时间,并且不会对我们的客户造成性能问题。

问题:我错过了重要的事情吗?临时表上的索引?

我更新了EXAMPLE表,因此我没有获得更多的规范化注释。每本书只有1个描述:)

2 个答案:

答案 0 :(得分:2)

您可以通过对SQL查询使用NOLOCKREADUNCOMITTED提示来阻止查询方面的阻止。

性能的真正问题可能是日志中的更改积累。您以5,000为一组批量更改的方法非常合理。因为您要在批处理表中设置更新,所以您也可以计算表中的批号,然后根据它进行循环。

答案 1 :(得分:0)

我会首先尝试你自己的建议,然后在运行更新之前索引临时表:

CREATE INDEX IDX_BookID ON #BookUpdates(BookID)

尝试使用索引而不使用索引,看看对运行时的影响是什么。如果您想避免影响用户进行此测试,请在工作时间以外运行(如果可以)或先将Book复制到另一个临时表并对其进行测试。

无论如何,考虑到音量,我预计你仍然会阻止其他进程。如果您无法在没有其他进程针对此表运行时安排更新(这将是理想的解决方案),则您现有的批量更新似乎是一个完全有效的解决方案。索引临时表也可能对此有所帮助,因此您可以增加批量大小而不会导致阻塞。