Я запускаю этот код, чтобы обновить количество на складе в зависимости от предыдущего дня (вчера). Он работает, но очень медленно. Каждый день система генерирует около 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
вы можете присоединиться к запросу для обновления. не могли бы вы упростить свою проблему и добавить некоторые данные?
SQL — это не язык программирования, поэтому не относитесь к нему как к одному; это язык запросов. Не думайте о том, что вы хотите сделать со строкой, подумайте о том, что вы хотите сделать со (всей) колонкой.
Подход, основанный на наборах, превзойдет такой курсор на несколько порядков.
Основываясь на вашем запросе, подход на основе набора может быть примерно таким. Отказ от ответственности: запрос не проверен.
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?
Я не уверен, так как не могу проверить. Если он генерирует один и тот же план выполнения, оба запроса должны быть взаимозаменяемыми и зависеть от предпочтений кодирования.
Нет. Однако у него есть то преимущество, что вы можете использовать несколько столбцов/выражений из объединенной таблицы. Версия со скалярным подзапросом (которую я опубликовал) может возвращать только одно значение.
Чтобы ускорить курсоры, нужно исключить курсоры. Вашему коду они не нужны. Но вы должны задать другой вопрос: «Как выразить этот курсор как операции на основе набора?» Этот вопрос должен включать примеры данных, желаемые результаты и объяснение того, что делает код.