Цикл и обновление без курсора - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть таблица, в которой хранятся остатки элементов.

CREATE TABLE itembalance (
   ItemID VARCHAR(15),
   RemainingQty INT,
   Cost Money,
   Id INT
)

Мне нужно убедиться, что при отправке элемента правильные остатки вычитаются из таблицы итераций.Я делаю это так:

DECLARE crsr CURSOR LOCAL FAST_FORWARD FOR 
        SELECT 
           itembalance.Cost, 
           itembalance.RemainingQty
           itembalance.Id
        FROM dbo.itembalance
        WHERE itembalance.ItemID = @v_item_to_be_updated AND RemainingQty > 0

OPEN crsr
FETCH crsr
INTO 
  @cost, 
  @qty, 
  @id

WHILE @@FETCH_STATUS = 0

BEGIN
IF @qty >= @qty_to_be_deducted
BEGIN
    UPDATE itembalance SET RemainingQty = RemainingQty - @qty_to_be_deducted WHERE Id = @id

    /*do something with cost*/ BREAK
END
ELSE
BEGIN
    UPDATE itembalance SET RemainingQty = 0 WHERE Id = @id
    /*do something with cost*/ SET @qty_to_be_deducted = @qty_to_be_deducted - @qty
END
FETCH crsr
INTO 
  @cost, 
  @qty, 
  @id


END
CLOSE crsr
DEALLOCATE crsr

Таблица может содержать тот же код товара, но с разной стоимостью.Этот код подходит для нескольких элементов, которые обновляются одновременно, но всякий раз, когда отправляется большое количество элементов / количеств, процесс становится очень медленным.Есть ли способ оптимизировать этот код?Я предполагаю, что курсор делает это медленно, поэтому я хочу исследовать другой код для этого процесса.

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

Ваш код не очень понятен относительно того, как и почему механизм требуется и работает.

Однако при условии, что у вас должно быть несколько записей с непогашенным балансом, и что вы должны рассматривать несколько записей последовательно как частьэтого механизма, тогда у вас есть два варианта решения этого в SQL (обработка в клиентском коде - другой вариант):

1) Используйте курсор, как вы сделали

2) Используйте временныйтаблицу или табличную переменную и итерируйте по ней - очень похоже на курсор, но, возможно, быстрее - вам нужно попробовать и посмотреть, например:

declare @TableVariable table (Cost money, RemainingQty int, Id int, OrderBy int, Done bit default(0))
declare @Id int, @Cost money, @RemainingQty int

insert into @TableVariable (Cost, RemainingQty, Id, OrderBy)
  SELECT 
    itembalance.Cost 
    , itembalance.RemainingQty
    , itembalance.Id
    , 1 /* Some order by condition */
  FROM dbo.itembalance
  WHERE itembalance.ItemID = @v_item_to_be_updated AND RemainingQty > 0

while exists (select 1 from @TableVariable where Done = 0) begin
  select top 1 @Id = id, @Cost = Cost, @RemainingQty
  from @TableVariable
  where Done = 0
  order by OrderBy

  -- Do stuff here

  update @TableVariable set Done = 1 where id = @Id
end

Однако код, который вы показали, не кажется, что он долженбыть медленным - возможно, вам не хватает соответствующих индексов и что одно обновление ItemId блокирует слишком много строк в таблице ItemBalance, что затем влияет на другие обновления ItemId.

0 голосов
/ 26 ноября 2018

Похоже, вам просто нужно CASE выражение:

UPDATE dbo.itembalance
SET Qty = CASE WHEN Qty >= @qty_to_be_deducted THEN Qty - @qty_to_be_deducted ELSE 0 END
WHERE ItemID = @v_item_to_be_updated
  --What is the difference between Qty and RemainingQty?
  --Why are you checking one and updating the other?
  AND RemainingQty > 0; 
...