UPDATE-no-op в операторе SQL MERGE
У меня есть таблица с некоторыми постоянными данными. Теперь, когда я запрашиваю его, у меня также есть довольно сложный CTE, который вычисляет значения, необходимые для результата, и мне нужно вставить отсутствующие строки в постоянную таблицу. В конце я хочу выбрать результат, состоящий из всех строк, идентифицированных CTE, но с данными из таблицы, если они уже были в таблице, и мне нужна информация о том, была ли строка вставлена или нет.
Упрощенное это работает следующим образом (следующий код запускается как обычный запрос, если вы хотите его попробовать):
-- Set-up of test data, this would be the persisted table
DECLARE @target TABLE (id int NOT NULL PRIMARY KEY) ;
INSERT INTO @target (id) SELECT v.id FROM (VALUES (1), (2)) v(id);
-- START OF THE CODE IN QUESTION
-- The result table variable (will be several columns in the end)
DECLARE @result TABLE (id int NOT NULL, new bit NOT NULL) ;
WITH Source AS (
-- Imagine a fairly expensive, recursive CTE here
SELECT * FROM (VALUES (1), (3)) AS Source (id)
)
MERGE INTO @target AS Target
USING Source
ON Target.id = Source.id
-- Perform a no-op on the match to get the output record
WHEN MATCHED THEN
UPDATE SET Target.id=Target.id
WHEN NOT MATCHED BY TARGET THEN
INSERT (id) VALUES (SOURCE.id)
-- select the data to be returned - will be more columns
OUTPUT source.id, CASE WHEN $action='INSERT' THEN CONVERT(bit, 1) ELSE CONVERT(bit, 0) END
INTO @result ;
-- Select the result
SELECT * FROM @result;
Мне не нравится часть WHEN MATCHED THEN UPDATE
, я предпочел бы оставить избыточное обновление, но тогда я не получаю строку результата в предложении OUTPUT
.
Является ли это наиболее эффективным способом выполнения такого рода заполнения и возврата данных?
Или было бы более эффективное решение без MERGE
, например, путем предварительного вычисления результата с помощью SELECT
, а затем выполнить INSERT
строк, которые являются new=0
? Мне трудно интерпретировать план запроса, поскольку он в основном сводится к "кластерному свариванию индексов", который довольно расплывчато для меня по производительности по сравнению с отдельным SELECT
, за которым следует вариант INSERT
. И мне интересно, действительно ли SQL Server (2008 R2 с CU1) достаточно умен, чтобы увидеть, что UPDATE
не работает (например, не требуется запись).
Ответы
Ответ 1
Вы можете объявить фиктивную переменную и установить ее значение в предложении WHEN MATCHED.
DECLARE @dummy int;
...
MERGE
...
WHEN MATCHED THEN
UPDATE SET @dummy = 0
...
Я считаю, что это должно быть дешевле, чем фактическое обновление таблицы.