2012-07-04 21 views
9

Tengo las líneas de orden de la tabla (OrderID int, LineIndex int,) y el parámetro de tabla de la misma estructura que define nuevas líneas de orden para una orden.TSQL - instrucción MERGE con la clave compuesta

Así que si tuviera las siguientes OrderLines

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

y la siguiente TVP

1001 1 yogurt 

quiero conseguir los siguientes OrderLines

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

es decir, toque las filas solo para una orden.

así que escribí mi consulta como esta

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; 

y elimina todos los otros OrderLines (no mencionados) para otras órdenes.

me trataron

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

pero tiene un error sintáctico.

¿Cómo debo volver a escribir mi consulta?

Respuesta

11

sólo tiene que utilizar el subconjunto correspondiente de OrderLines como un objetivo:

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; 

Y here's a SQL Fiddle a prueba.

1

Para los principiantes, solo las columnas de la tabla de destino se pueden usar en la condición de fusión adicional WHEN NOT MATCHED BY SOURCE (está en MSDN).

Y creo que es normal que pierda todas las entradas adicionales de la tabla de destino, ya que no coinciden con nada en la fuente.

Debe volver a escribir su consulta eliminando primero la cláusula WHEN NOT MATCHED BY SOURCE y luego eliminando por separado las filas extra/innecesarias.

Entonces, usted necesita para obtener todos los registros que se actualizan o insertados en la tabla de destino añadiendo:

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 

Ahora en @OutputTable que tienen todas las claves que se han actualizado o consignarán bien en la tabla de destino (aviso la cláusula OUTPUT).

Sólo se necesita ahora para ver qué filas de la tabla de destino, que sólo coincide con las llaves de la @OrderLines, no están en la declaración @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 

Lo que está haciendo aquí (piensa es correcto) es en realidad lo que quería eliminar en primer lugar. La combinación interna filtra el conjunto de resultados a @OrderLines (por lo que solo las filas con esas teclas) y la izquierda se unen con la cláusula where hace una unión anti semi, para obtener filas en la tabla de destino que no se vean afectadas por la instrucción MERGE (insertar o actualización) pero todavía tienen las claves que se encuentran en la tabla fuente (@OrderLines).

Debería estar en lo cierto ... Házmelo saber después de que lo pruebe.

Es posible que desee envolver todo esto (MERGE + DELETE) dentro de una transacción, si decide seguir este enfoque.

Cuestiones relacionadas