Низкая производительность для курсора обновления

Я запускаю этот код, чтобы обновить количество на складе в зависимости от предыдущего дня (вчера). Он работает, но очень медленно. Каждый день система генерирует около 50 тысяч строк, каждая из которых соответствует адресу (позиции) на складе. Таким образом, курсор получает каждый адрес, а затем ищет количество за день до этого, а затем обновляет.

Пример:

Адрес 01B134010 на 04.08.2019 имел количество 500

Адрес 01B134010 на 04.09.2019 имел количество 450

Итак, когда код запускается, он обновляет столбец «DIF_DIA» на -50.

Сегодня я запустил код, и только для одной ветки (47 тыс. + строк) потребовалось более 1 часа.

Есть ли лучший/быстрый способ сделать это?

DECLARE @ARMAZEM    VARCHAR(2),
        @FILIAL     VARCHAR(3),
        @CLIENTE    VARCHAR(6),
        @GRPESTOQUE VARCHAR(6),
        @DATA       AS VARCHAR(8),
        @ENDERECO as VARCHAR(30),
        @DIFERENCA AS FLOAT

-- reinicia variaveis
SET @armazem = ''
SET @FILIAL = ''
SET @CLIENTE = ''
SET @GRPESTOQUE = ''
SET @DATA = ''
SET @ENDERECO = ''
SET @DIFERENCA = ''

DECLARE CUR_UPD_DIF_DIA CURSOR FOR
  SELECT CODIGO_ARMAZEM,
         CODIGO_FILIAL,
         CODIGO_CLIENTE,
         GRP_EST_COD,
         DATA_SALDO,
         CODIGO_ENDERECO
  FROM   WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO
  WHERE  DATA_SALDO >= '20190401'
  and CODIGO_FILIAL = '106'
         AND DIF_DIA IS NULL
  FOR UPDATE OF DIF_DIA

OPEN CUR_UPD_DIF_DIA

FETCH NEXT FROM CUR_UPD_DIF_DIA INTO @ARMAZEM,
                               @FILIAL,
                               @CLIENTE,
                               @GRPESTOQUE,
                               @DATA, 
                               @ENDERECO

WHILE @@FETCH_STATUS = 0
  BEGIN

      -- pega a quantidade deste endereço ontem
      SELECT @DIFERENCA = IsNull(QUANTIDADE_PALLET,0)
      FROM   WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO T1
      WHERE  T1.DATA_SALDO = dateadd(day, -1 , @DATA)
             AND T1.GRP_EST_COD     = @GRPESTOQUE
             AND T1.CODIGO_CLIENTE  = @CLIENTE
             AND T1.CODIGO_ARMAZEM  = @ARMAZEM
             AND T1.CODIGO_ENDERECO = @ENDERECO
             AND T1.CODIGO_FILIAL   = @FILIAL

      -- e atualiza o endereço na data de hoje, tendo o valor da DIF_DIA a quantidade atual - a quantidade de ontem, assim, tem a diferença.
      -- se positivo, incrementou o endereço. Se negativo, diminuiu do endereço. Válido, na prática, somente para BLOCADO pois PP sempre terá apenas "1" de quantidade
      UPDATE WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO
      SET    DIF_DIA = (QUANTIDADE_PALLET - @DIFERENCA)
      WHERE  CURRENT OF CUR_UPD_DIF_DIA

      SET @DIFERENCA = 0

      FETCH NEXT FROM CUR_UPD_DIF_DIA INTO @ARMAZEM,
                                     @FILIAL,
                                     @CLIENTE,
                                     @GRPESTOQUE,
                                     @DATA, @ENDERECO
  END

CLOSE CUR_UPD_DIF_DIA

DEALLOCATE CUR_UPD_DIF_DIA

Чтобы ускорить курсоры, нужно исключить курсоры. Вашему коду они не нужны. Но вы должны задать другой вопрос: «Как выразить этот курсор как операции на основе набора?» Этот вопрос должен включать примеры данных, желаемые результаты и объяснение того, что делает код.

Gordon Linoff 09.04.2019 21:34

вы можете присоединиться к запросу для обновления. не могли бы вы упростить свою проблему и добавить некоторые данные?

Hasan Mahmood 09.04.2019 21:36

SQL — это не язык программирования, поэтому не относитесь к нему как к одному; это язык запросов. Не думайте о том, что вы хотите сделать со строкой, подумайте о том, что вы хотите сделать со (всей) колонкой.

Larnu 09.04.2019 21:39
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
3
157
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Подход, основанный на наборах, превзойдет такой курсор на несколько порядков.

Основываясь на вашем запросе, подход на основе набора может быть примерно таким. Отказ от ответственности: запрос не проверен.

UPDATE WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO
SET    DIF_DIA = (QUANTIDADE_PALLET - (
    SELECT IsNull(QUANTIDADE_PALLET,0)
    FROM   WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO T1
    WHERE  T1.DATA_SALDO = dateadd(day, -1 , WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.DATA_SALDO)
    AND    T1.GRP_EST_COD     = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.GRP_EST_COD
    AND    T1.CODIGO_CLIENTE  = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_CLIENTE
    AND    T1.CODIGO_ARMAZEM  = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_ARMAZEM
    AND    T1.CODIGO_ENDERECO = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_ENDERECO
    AND    T1.CODIGO_FILIAL   = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_FILIAL
))
WHERE  DATA_SALDO >= '20190401'
and CODIGO_FILIAL = '106'
AND DIF_DIA IS NULL

Это еще один вариант оптимизации ОБНОВЛЕНИЯ путем выполнения одной операции вместо последовательного выполнения строк за строкой. Надеюсь, я уловил правильную логику.

UPDATE atual
SET    DIF_DIA = atual.QUANTIDADE_PALLET - IsNull(ontem.QUANTIDADE_PALLET,0)
FROM WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO atual
LEFT
JOIN WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO ontem ON atual.DATA_SALDO      = dateadd(day, 1 , ontem.DATA_SALDO)
                                                    AND atual.GRP_EST_COD     = ontem.GRP_EST_COD    
                                                    AND atual.CODIGO_CLIENTE  = ontem.CODIGO_CLIENTE 
                                                    AND atual.CODIGO_ARMAZEM  = ontem.CODIGO_ARMAZEM 
                                                    AND atual.CODIGO_ENDERECO = ontem.CODIGO_ENDERECO
                                                    AND atual.CODIGO_FILIAL   = ontem.CODIGO_FILIAL  
WHERE atual.DATA_SALDO >= '20190401'
AND   atual.CODIGO_FILIAL = '106'
AND   atual.DIF_DIA IS NULL;

По скорости эта версия (с JOIN) быстрее, чем другая опция с select?

Luiz Carlos 09.04.2019 23:13

Я не уверен, так как не могу проверить. Если он генерирует один и тот же план выполнения, оба запроса должны быть взаимозаменяемыми и зависеть от предпочтений кодирования.

Luis Cazares 10.04.2019 15:59

Нет. Однако у него есть то преимущество, что вы можете использовать несколько столбцов/выражений из объединенной таблицы. Версия со скалярным подзапросом (которую я опубликовал) может возвращать только одно значение.

Gert-Jan 09.08.2019 16:55

Другие вопросы по теме