2012-07-04 17 views
9

Sono presenti tabelle OrderLines (OrderID int, LineIndex int) e parametri con valori di tabella della stessa struttura che definiscono nuove righe di ordine per un ordine.TSQL - Istruzione MERGE con chiave composita

Quindi, se ho avuto le seguenti OrderLines

1000 1 bread 
1000 2 milk 
1001 1 oil 
1001 2 yogurt 
1002 1 beef 
1002 2 pork 

e il seguente TVP

1001 1 yogurt 

voglio ottenere i seguenti OrderLines

1000 1 bread 
1000 2 milk 
1001 1 yogurt 
1002 1 beef 
1002 2 pork 

Vale a dire tocca le righe solo per un ordine.

Così ho scritto la mia domanda come questo

MERGE 
    [OrderLines] AS [Target] 
USING 
(
    SELECT 
     [OrderID], [LineIndex], [Data] 
    FROM 
     @OrderLines 
) 
AS [Source] ([OrderID], [LineIndex], [Data]) 
ON ([Target].[OrderID] = [Source].[OrderID]) AND ([Target].[LineIndex] = [Source].[LineIndex]) 
WHEN MATCHED THEN 
    UPDATE 
    SET 
     [Target].[Data] = [Source].[Data] 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT 
     ([OrderID], [LineIndex], [Data]) 
    VALUES 
     ([Source].[OrderID], [Source].[LineIndex], [Source].[Data]) 
WHEN NOT MATCHED BY SOURCE THEN 
    DELETE; 

ed elimina tutti gli altri OrderLines (non citati) per altri Ordini.

ho cercato

WHEN NOT MATCHED BY SOURCE AND ([Target].[OrderID] = [Source].[OrderID]) THEN 

ma ha un errore sintattico.

Come dovrei riscrivere la mia richiesta?

risposta

11

basta usare il relativo sottoinsieme di OrderLines come un obiettivo:

WITH AffectedOrderLines AS ( SELECT * FROM OrderLines WHERE OrderID IN (SELECT OrderID FROM @OrderLines) ) 
MERGE 
    AffectedOrderLines AS [Target] 
USING 
(
    SELECT 
     [OrderID], [LineIndex], [Data] 
    FROM 
     @OrderLines 
) 
AS [Source] ([OrderID], [LineIndex], [Data]) 
ON ([Target].[OrderID] = [Source].[OrderID]) AND ([Target].[LineIndex] = [Source].[LineIndex]) 
WHEN MATCHED THEN 
    UPDATE 
    SET 
     [Target].[Data] = [Source].[Data] 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT 
     ([OrderID], [LineIndex], [Data]) 
    VALUES 
     ([Source].[OrderID], [Source].[LineIndex], [Source].[Data]) 
WHEN NOT MATCHED BY SOURCE THEN 
    DELETE; 

E here's a SQL Fiddle per testare.

1

Per gli avviatori, è possibile utilizzare solo le colonne della tabella di destinazione nella condizione di unione aggiuntiva WHEN NOT MATCHED BY SOURCE (è su MSDN).

E penso che sia normale che si perdano tutte le voci extra dalla tabella di destinazione, perché non corrispondono nulla nella fonte.

È necessario riscrivere la query cancellando prima la clausola WHEN NOT MATCHED BY SOURCE e quindi eliminando le righe extra/non necessarie separatamente.

Quindi, è necessario per ottenere tutte le voci sono aggiornati o inseriti nella tabella di destinazione con l'aggiunta di:

DECLARE @OutputTable table(OrderId INT, OrderLine INT); 

...Your entire MERGE 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT 
     ([OrderID], [LineIndex], [Data]) 
    VALUES 
     ([Source].[OrderID], [Source].[LineIndex], [Source].[Data]) 
OUTPUT INSERTED.OrderId, INSERTED.LineIndex INTO @OutputTable 

Ora in @OutputTable avete tutte le chiavi che erano o aggiornati o inseriti nella tabella di destinazione (avviso la clausola OUTPUT).

Hai solo bisogno subito per vedere quale righe dalla tabella di destinazione, che corrispondono solo le chiavi dal @OrderLines, non sono nella dichiarazione @OutputTable' and delete them (so they haven't been updated nor inserted by the MERGE`):

DELETE A 
FROM [OrderLines] AS A 
INNER JOIN @OrderLines AS B 
ON B.OrderId = A.OrderId AND B.LineIndex = A.LineIndex 
LEFT OUTER JOIN @OutputTable AS C 
ON C.OrderId = A.OrderId AND C.OrderLine = A.LineIndex 
WHERE C.OrderId IS NULL AND C.OrderLine IS NULL 

Che cosa stai facendo qui (si pensi è giusto) è in realtà ciò che volevi eliminare in primo luogo. Il join interno filtra il set di risultati su @OrderLines (quindi solo le righe con quelle chiavi) e il join sinistro insieme alla clausola where sta facendo un anti semi join, per ottenere righe nella tabella di destinazione che non sono influenzate dall'istruzione MERGE (inserire o aggiornamento) ma hanno ancora le chiavi che si trovano nella tabella di origine (@OrderLines).

Dovrebbe essere giusto ... Fammi sapere dopo averlo testato.

Si consiglia di avvolgere tutto questo (MERGE + DELETE) all'interno di una transazione, se si decide di seguire questo approccio.

Problemi correlati