Ускорьте СЛИЯНИЕ в SQL Server миллионов строк

#sql #sql-server #indexing #merge #sqlperformance

Вопрос:

Я пытаюсь вставить/обновить миллионы строк в таблицу, которая уже содержит миллионы строк.

Целевая таблица содержит столбец идентификатора (PK) и ряд других столбцов.

Я вставляю данные из csv во временную таблицу #temp (те же столбцы, что и в целевой таблице, за исключением идентификатора).

Затем я пытаюсь объединить их, но слияние происходит безумно медленно (несколько часов для 10-метровых строк).

 -- Create temporal table #temp with almost all the columns in target_table
SELECT ColumnA,
        ColumnB,
        ColumnC,
        ColumnD,
        ColumnE,
        ColumnF,
        ColumnG,
        ColumnH
INTO #temp
FROM target_table
WHERE 1 = 2;

<HERE I DO A BULK INSERT INTO #temp WHICH ONLY TAKES A FEW SECONDS>

-- -- Merge into target_table
MERGE target_table AS TARGET
USING #temp AS SOURCE
ON (
            TARGET.ColumnA = SOURCE.ColumnA
        AND
            TARGET.ColumnB = SOURCE.ColumnB
        AND
            TARGET.ColumnC = SOURCE.ColumnC
        AND
            TARGET.ColumnE = SOURCE.ColumnE
    )
    
WHEN MATCHED
    AND (
            (TARGET.ColumnG IS NULL AND SOURCE.ColumnG IS NOT NULL OR TARGET.ColumnG <> SOURCE.ColumnG)
            OR
            (TARGET.ColumnH IS NULL AND SOURCE.ColumnH IS NOT NULL OR TARGET.ColumnH <> SOURCE.ColumnH)
        )
    THEN
    UPDATE SET TARGET.ColumnG = SOURCE.ColumnG, TARGET.ColumnH = SOURCE.ColumnH

WHEN NOT MATCHED BY TARGET
    THEN
    INSERT (ColumnA, ColumnB, ColumnC, ColumnD, ColumnE, ColumnF, ColumnG, ColumnH)
    VALUES (SOURCE.ColumnA, SOURCE.ColumnB, SOURCE.ColumnC, SOURCE.ColumnD, SOURCE.ColumnE, SOURCE.ColumnF,
            SOURCE.ColumnG, SOURCE.ColumnH);
 

После массовой вставки план выполнения СЛИЯНИЯ содержит некоторые очень дорогие сортировки (41% от общей стоимости).

Есть какие-либо рекомендации о том, как ускорить слияние?

Спасибо!

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

1. Пожалуйста, добавьте свои определения таблиц и индексов в свой вопрос и поделитесь планом запроса с помощью brentozar.com/pastetheplan , без чего мы не можем оказать никакой определенной помощи. Вероятно, вам нужен кластеризованный индекс для обеих таблиц (ColumnA, ColumnB, ColumnC, ColumnE)

2. Что такое «миллионы» в относительном выражении? Если вы обновляете/вставляете так много, что почти все данные переписываются, один из вариантов, который следует рассмотреть, — это просто полностью создать новую таблицу ( SELECT существующие данные, объединить с новыми данными, затем массово вставить их), а затем использовать или sp_rename или truncate и alter table .. switch полностью заменить исходную таблицу. Будет ли это быстрее или нет, зависит от того, но массовую вставку трудно превзойти с точки зрения скорости по сравнению с более сложными операциями, а транзакционное переключение таблиц трудно превзойти с точки зрения простоя.

Ответ №1:

После вставки данных в временную таблицу #вы можете создать кластеризованный индекс для временной таблицы:

 CREATE CLUSTERED INDEX tbl_temp_index ON #temp (ColumnA, ColumnB, ColumnC, ColumnE);  
 

Это повышает производительность во время процесса объединения.
Было бы хорошо, если бы вы могли создать такой же индекс и для target_table.

Если вы можете обновить значения NULL до значения по умолчанию на ColumnG и ColumnH , чтобы вы могли заменить это условие:

  (TARGET.ColumnG IS NULL AND SOURCE.ColumnG IS NOT NULL OR TARGET.ColumnG <> SOURCE.ColumnG)
 OR
 (TARGET.ColumnH IS NULL AND SOURCE.ColumnH IS NOT NULL OR TARGET.ColumnH <> SOURCE.ColumnH)
 

Для:

     (TARGET.ColumnG <> SOURCE.ColumnG)
    OR
    (TARGET.ColumnH <> SOURCE.ColumnH)  
 

Я протестировал этот запрос на своем компьютере. Я создал эти таблицы и вставил в таблицу 10 миллионов записей образцов. Но я не знаю , каков тип ваших ColumnsA ColumnsB полей, я установил целочисленный тип для этих полей. Продолжительность выполнения этого запроса ( create temp file, insert 10 M from CSV, and merge tables ) в приложении «Мой компьютер». 20-25 секунд.