SQL Server: слияние в итерациях

#sql #sql-server #merge #sql-server-2017

#sql #sql-сервер #слияние #sql-server-2017

Вопрос:

Мне нужно объединить миллионы строк в таблицу. Целевая таблица имеет триггер ПОСЛЕ ОБНОВЛЕНИЯ. Весь процесс потребляет намного больше памяти, чем я хотел бы выделить, а база данных tempdb занимает место на диске.

Я бы хотел, чтобы команда СЛИЯНИЯ выполнялась партиями по 100 000 записей одновременно. Поскольку SET ROWCOUNT устарел, а курсоры неэффективны, я не уверен, каким будет наилучший подход для этого.

Комментарии:

1. Я не понимаю, как замена на update с merge изменит производительность триггера.

Ответ №1:

Подход, ориентированный на набор, был бы лучшим способом эффективного выполнения запроса. Итак, то, что вы делаете, кажется мне просто прекрасным. Если он использует временную базу данных и т. Д., Необходимо будет знать, выполняете ли вы какие-либо операции по строке за строкой, которые замедляются. Как правило, СЛИЯНИЕ представляет собой один оператор и, следовательно, является эффективным.

Другие вещи, которые следует учитывать, это то, что если у вас есть индексы в целевой таблице, вы можете удалить эти индексы, а затем запустить СЛИЯНИЕ с последующим воссозданием индексов.

Возвращаясь к вашему вопросу, вы можете разделить на пакеты, используя оператор mod и запустив СЛИЯНИЕ в цикле

например:

 declare @i int
select @i=count(*)/10 from source


while @i>0
begin
   merge
    into dest d
   using (select *
            from source
           where id%10000=i --here id is the primary key of the source table
          ) s
      on d.id=s.id
    when matched
     set ...
    when not matched
     insert...
    ...rest of the insert/update logic here    

    set @i=@i-1
end
  

Ответ №2:

Попробуйте цикл WHILE

 DECLARE @I INT = 1
WHILE (@I > 0)
BEGIN
    ;MERGE INTO Dst USING (
         SELECT TOP 1000
         FROM Src
         WHERE NotUpdated
    )
    ...
    
    SET @I = @@ROWCOUNT
END
  

Комментарии:

1. чтобы не объединять одни и те же 1000 строк снова и снова, вы используете WHERE NotUpdated . Как бы вы реализовали это на Src, в середине слияния?

2. Это зависит от варианта использования. Вы можете присоединиться к таблице Dst, чтобы проверить, была ли обновлена строка. Преимущество этого решения заключается в том, что нет проблем с добавлением записей в Src или Dst во время цикла.

3. Это интересная идея. Вопрос: будет ли срабатывать триггер после выполнения каждой итерации цикла или (как я боюсь), только после выполнения всей транзакции?

4. Я протестировал это, и триггер запускается для каждой итерации цикла.