#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 секунд.