Медленные обновления SQL внутри курсора - PullRequest
3 голосов
/ 30 июля 2009

Я добавил новый столбец 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 по следующим правилам:

Моя проблема в том, что написанный мной скрипт использует 15 минут для 26500 строк на моем testServer.Вот оно:

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 ]

8 голосов
/ 30 июля 2009

Вы не хотите использовать курсор, вы хотите сделать это как набор, например так:

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

Это позволит использовать функцию ROW_NUMBER в SQL Server дляполучить правильный счет по каждой строке.Используя UPDATE...FROM, мы можем создать inner join для обновления.Это значительно эффективнее, чем курсор.

См., Курсоры добавляют итеративную возможность в процедурный и основанный на языке (SQL) язык.Эти двое плохо играют вместе.Этот оператор update вызывается для каждой строки (и открытия / совершения транзакции для загрузки).Если вы делаете все это в одном операторе, SQL может распараллелить это, чтобы сделать его намного быстрее.

Стандартное правило таково: Используйте курсоры как можно реже. В некоторых случаях существует несколькоони необходимы, но если вы не занимаетесь огромным количеством администрирования SQL в день, сомнительно, что вы когда-нибудь сталкивались с такими случаями.

1 голос
/ 30 июля 2009

Нечто подобное, не проверено

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
0 голосов
/ 30 июля 2009
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 или новее)

0 голосов
/ 30 июля 2009

Если вам действительно нужно использовать курсоры, определите курсор с помощью некоторых атрибутов, например:

DECLARE _cursor CURSOR LOCAL FAST_FORWARD FOR
....
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...