光标内的SQL更新速度慢

时间:2009-07-30 11:20:06

标签: sql sql-server performance tsql

我在表格中添加了一个新列packageNo

 create table Packages(
  id varchar(20) primary key,  -- ok, I know :)
  orderNo uniqueIdentifier not null,
  orderlineNo int not null, 
  packageNo int not null default(0)
)

现在我想使用以下规则生成packageNo:  

    为每个订单重置
 
    上升为订单,订单行

我的问题是我写的脚本在我的testServer上使用了15分钟的26500行。 这是:

set NoCount ON
declare @Counter int
declare @handledCounter int
declare @currentorder uniqueIdentifier
declare @fetchedOrder uniqueIdentifier
declare @fetchedId varchar(20) -- will using PK speed up things?

declare PackageNo_Cursor cursor  for 
  select orderNo, id from packages order by orderNo, orderlineNo for update of packageNo

open PackageNo_Cursor
fetch next from PackageNo_Cursor into @fetchedOrder, @fetchedId 

set @currentOrder = @fetchedOrder
set @counter = 0
set @handledCounter = 0
while @@fetch_status = 0
 begin 
     if (@currentOrder <> @fetchedOrder)
       begin  -- reset counter for each order
        set @currentOrder = @fetchedOrder
        set @counter = 0
       end
     set @counter = @counter + 1
     set @handledCounter = @handledCounter +1
     if (@handledCounter % 50 = 0)
      begin
        print 'handled = ' + cast(@handledCounter as varchar) 
      end
   update packages set packageNo = @counter where current of PackageNo_Cursor    
     fetch next from PackageNo_Cursor into @fetchedOrder, @fetchedId 
  end

close PackageNo_Cursor
deallocate PackageNo_Cursor

这应该导致:

id   - orderno - lineNo - packageNo (what I need to set)  
ean1 - guid1   - 1      - 1  
ean2 - guid1   - 2      - 2   
ean3 - guid2   - 1      - 1  
ean15- guid2   - 3      - 2  
ean15- guid2   - 4      - 3

我可以更快地运行吗?

4 个答案:

答案 0 :(得分:8)

您不想使用光标,您希望将其作为一个集合执行,如下所示:

update p set
    packageNo = r.packageNo
from
    packages p
    inner join 
      (select orderNo, orderLineNo, 
       ROW_NUMBER() OVER(PARTITION BY orderNo ORDER BY orderLineNo) as packageNo
       from packages) r on
        p.orderNo = r.orderNo
        and p.orderLineNo = r.orderLineNo

这将利用SQL Server的ROW_NUMBER函数为每行提供正确的计数。使用UPDATE...FROM,我们可以创建一个inner join来更新。这比光标更有效率。

请参阅,游标为基于过程和基于集合的语言(SQL)添加迭代功能。这两个人在一起打得不好。正在为每一行调用update语句(并打开/提交事务,以引导)。如果你在一个语句中完成所有操作,SQL可以将其并行化以使其更快。

标准规则是这样的:尽可能少地使用游标。有一些边缘情况需要它们,但如果你每天都没有进行大量的SQL管理,那就是怀疑你会遇到过这些案件。

答案 1 :(得分:1)

类似的东西,没有经过测试

update
    p
set
    packageNo = p2.Ranking
from
    packages p
    JOIN
    (SELECT
        orderNo, orderlineNo,
        ROW_NUMBER() OVER (PARTITION BY orderNo ORDER BY orderlineNo) AS Ranking
    FROM
        packages) p2 ON p.orderNo = p2.orderNo AND p.orderlineNo= p2.orderlineNo

答案 2 :(得分:0)

WITH cteA AS (
    SELECT 
        packageNo, 
        row_number() over (partition by orderNo order by orderNo, orderlineNo) rn
    FROM packages
)

UPDATE cteA
SET packageNo = rn - 1;

您还应该在orderNo,orderlineNo。

上创建聚簇索引

(我希望您使用的是sql2005或更新版本)

答案 3 :(得分:-1)

如果您确实需要使用游标,请使用某些属性定义光标,如下所示:

DECLARE _cursor CURSOR LOCAL FAST_FORWARD FOR
....