Я пытаюсь использовать MERGE
оператор для вставки или удаления строк из таблицы, но я хочу воздействовать только на подмножество этих строк. В документации MERGE
есть довольно строго сформулированное предупреждение:
Важно указать только столбцы из целевой таблицы, которые используются для сопоставления. То есть укажите столбцы из целевой таблицы, которые сравниваются с соответствующим столбцом исходной таблицы. Не пытайтесь улучшить производительность запросов, отфильтровывая строки в целевой таблице в предложении ON, например, указав AND NOT target_table.column_x = value. Это может привести к неожиданным и неправильным результатам.
но это именно то, что я должен сделать, чтобы сделать свою MERGE
работу.
Данные, которые у меня есть, представляют собой стандартную таблицу «многие ко многим», в которой элементы объединяются в категории (например, какие элементы включены в какие категории) следующим образом:
CategoryId ItemId
========== ======
1 1
1 2
1 3
2 1
2 3
3 5
3 6
4 5
Что мне нужно сделать, так это эффективно заменить все строки в определенной категории новым списком элементов. Моя первая попытка сделать это выглядит так:
MERGE INTO CategoryItem AS TARGET
USING (
SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT ( CategoryId, ItemId )
VALUES ( 2, ItemId )
WHEN NOT MATCHED BY SOURCE AND TARGET.CategoryId = 2 THEN
DELETE ;
Это кажется , что работает в моих тестах, но я делаю именно то , что MSDN явно предупреждает меня не делать. Это заставляет меня беспокоиться о том, что позже я столкнусь с неожиданными проблемами, но я не вижу другого способа сделать так, чтобы мои MERGE
аффекты вступали только в строки с определенным значением поля ( CategoryId = 2
) и игнорировали строки из других категорий.
Есть ли «более правильный» способ добиться такого же результата? И каковы «неожиданные или неправильные результаты», о которых меня предупреждает MSDN?
источник
Ответы:
MERGE
Оператор имеет сложный синтаксис и еще более сложная реализацию, но по существу идея состоит в том, чтобы соединить две таблицы, отфильтровать до строк , которые должны быть изменены (вставлено, обновлено или удалено), а затем выполнить требуемые изменения. Учитывая следующие примерные данные:цель
Источник
Желаемый результат - заменить данные в цели данными из источника, но только для
CategoryId = 2
. СледуяMERGE
приведенному выше описанию , мы должны написать запрос, который соединяет источник и цель только по ключам и фильтрует строки только вWHEN
предложениях:Это дает следующие результаты:
План выполнения:
Обратите внимание, что обе таблицы полностью отсканированы. Мы можем подумать, что это неэффективно, потому что
CategoryId = 2
в целевой таблице будут затронуты только строки . Вот тут и появляются предупреждения в Books Online. Одна из ошибочных попыток оптимизации, чтобы коснуться только нужных строк в цели:Логика в
ON
предложении применяется как часть объединения. В этом случае объединение является полным внешним объединением (см. Эту запись Books Online, чтобы узнать почему). Применение проверки для категории 2 к целевым строкам как части внешнего соединения в конечном итоге приводит к удалению строк с другим значением (поскольку они не соответствуют источнику):Основной причиной является та же причина, по которой предикаты ведут себя по-разному в предложении внешнего соединения
ON
, чем если они указаны вWHERE
предложении.MERGE
Синтаксис (и реализация присоединиться в зависимости от положений , указанных) просто сделать это труднее , чтобы увидеть , что это так.Руководство в Books Online (вспененное в оптимизации производительности ввода) предлагает рекомендацию , которые обеспечат правильную семантику выражаются с помощью
MERGE
синтаксиса, без пользователей обязательно должны иметь , чтобы понять все детали реализации или учетную запись для способов , в которых оптимизатор может законно переставляет вещи по причинам эффективности исполнения.Документация предлагает три возможных способа реализации ранней фильтрации:
Указание условия фильтрации в
WHEN
предложении гарантирует правильные результаты, но может означать, что из исходной и целевой таблиц будет прочитано и обработано больше строк, чем строго необходимо (как видно из первого примера).Обновление через представление, которое содержит условие фильтрации, также гарантирует правильные результаты (поскольку измененные строки должны быть доступны для обновления через представление), но для этого требуется выделенное представление, и такое, которое следует нечетным условиям для обновления представлений.
Использование общего табличного выражения сопряжено с риском, связанным с добавлением предикатов в
ON
предложение, но по несколько иным причинам. Во многих случаях это будет безопасно, но для этого требуется экспертный анализ плана выполнения (и обширное практическое тестирование). Например:Это дает правильные результаты (не повторяется) с более оптимальным планом:
План считывает только строки для категории 2 из целевой таблицы. Это может быть важным фактором производительности, если целевая таблица велика, но слишком легко ошибиться, используя
MERGE
синтаксис.Иногда проще написать
MERGE
отдельные операции DML. Этот подход может даже работать лучше, чем одинMERGE
, что часто удивляет людей.источник