UPDATE-no-op in MERGE SQL
-
24-10-2019 - |
Domanda
Ho una tabella con alcuni dati persistenti in esso. Ora, quando interrogo esso, ho anche un CTE abbastanza complesso che calcola i valori necessari per il risultato e ho bisogno di inserire righe mancanti nella tabella persistente. Alla fine voglio selezionare il risultato che consiste di tutte le righe individuate dalla CTE ma con i dati della tabella se fossero già nella tabella, e ho bisogno le informazioni se una riga è stata appena inserito o meno.
semplificato Questa opere come questa (i seguenti percorsi di codice come una query normale se vi piace di provarlo):
-- 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;
Non mi piace la parte WHEN MATCHED THEN UPDATE
, preferirei lasciare l'aggiornamento ridondante via, ma poi non ho ricevuto la riga del risultato nella clausola OUTPUT
.
E 'questo il modo più efficace per fare questo tipo di completamento e restituzione di dati?
O ci sarebbe una soluzione più efficiente senza MERGE
, ad esempio pre-computing il risultato con un SELECT
e quindi eseguire un INSERT
delle righe che sono new=0
? Ho difficoltà che interpretano il piano di query in quanto si riduce sostanzialmente verso il basso per un "Clustered Index Merge", che è piuttosto vago per me prestazioni-saggio rispetto al SELECT
separata seguita da variante INSERT
. E mi chiedo se SQL Server (2008 R2 con CU1) è in realtà abbastanza intelligente per capire che la UPDATE
è un no-op (richiesto ad esempio senza scrittura).
Soluzione
Si potrebbe dichiarare una variabile fittizia e impostare il suo valore nella clausola WHEN MATCHED.
DECLARE @dummy int;
...
MERGE
...
WHEN MATCHED THEN
UPDATE SET @dummy = 0
...
Credo che dovrebbe essere meno costoso che l'aggiornamento tabella effettiva.