Update-No-OP en la declaración SQL Merge
-
24-10-2019 - |
Pregunta
Tengo una tabla con algunos datos persistentes. Ahora, cuando lo considero, también tengo un CTE bastante complejo que calcula los valores requeridos para el resultado y necesito insertar filas faltantes en la tabla persistente. Al final, quiero seleccionar el resultado que consta de todas las filas identificadas por el CTE pero con los datos de la tabla si ya estaban en la tabla, y necesito la información si una fila se ha insertado o no.
Simplified esto funciona como este (el siguiente código se ejecuta como una consulta normal si desea probarlo):
-- 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;
No me gusta el WHEN MATCHED THEN UPDATE
Parte, prefiero dejar la actualización redundante lejos, pero luego no obtengo el resultado de la fila en el OUTPUT
cláusula.
¿Es esta la forma más eficiente de hacer este tipo de datos de completar y devolver?
O habría una solución más eficiente sin MERGE
, por ejemplo, previamente computando el resultado con un SELECT
y luego realizar un INSERT
de las filas que son new=0
? Tengo dificultades para interpretar el plan de consulta, ya que básicamente se reduce a una "fusión del índice agrupado" que es bastante vago para mí en cuanto al rendimiento en comparación con el separado SELECT
seguido por INSERT
variante. Y me pregunto si SQL Server (2008 R2 con CU1) es realmente lo suficientemente inteligente como para ver que el UPDATE
es un no-op (por ejemplo, no se requiere escritura).
Solución
Puede declarar una variable ficticia y establecer su valor en la cláusula coincidente.
DECLARE @dummy int;
...
MERGE
...
WHEN MATCHED THEN
UPDATE SET @dummy = 0
...
Creo que debería ser menos costoso que la actualización real de la tabla.