web-dev-qa-db-fra.com

Façon de fusionner avec la source de mise à jour lorsque la cible ne correspond pas?

J'essaie d'écrire une procédure stockée qui prendra de nouvelles valeurs à partir d'une table Temp, de les fusionner dans une table réelle, ce qui me donne l'ID des lignes insérées et les lignes existantes déjà correspondant à ses valeurs.

Maintenant, de ce que je vois, la fonction de fusion ne semble pas soutenir ce que j'essaie de faire, à moins que je ne le fais pas mal (complètement probable, je ne suis pas un gars SQL - .NET). Quoi qu'il en soit, je viens d'apprendre vraiment sur la fusion de ce soir, et cela semblait être ce que je devrais utiliser au début, mais cela ne semble pas avoir la fonctionnalité dont j'ai besoin, car je ne peux pas simplement obtenir l'identifiant qui existait d'avant, dont j'ai besoin pour le reste d'une procédure stockée. Mon objectif principal était de voir si je pouvais obtenir cela pour travailler, mais l'autre problème est que je dois être sûr que le code gère les appels simultanés potentiels à une procédure stockée qui traverserait la fusion. Je sais que je n'ai pas de verrouillage là-bas maintenant, mais j'essayais simplement de le faire fonctionner à ce stade. Alors, qu'est-ce que j'ai ....

Installer

DECLARE @Metadata TABLE
(
    MetadataId INT PRIMARY KEY IDENTITY(1,1),
    MetadataTypeId INT,
    MetadataTypeValueId INT
)

INSERT INTO @Metadata 
VALUES
(1,23),(2,32),(2,33),(2,43),(1,24),(3,33),(1,20)

--Original Table Data
SELECT * FROM @Metadata

DECLARE @MergeMetadata TABLE
(
    MetadataId INT,
    MetadataTypeId INT,
    MetadataTypeValueId INT
)

INSERT INTO @MergeMetadata (MetadataTypeId, MetadataTypeValueId)
VALUES
(2,32),(2,35),(3,34),(4,1),(1,23)

--Metadata that needs added and/or ID'd if exists
SELECT * FROM @MergeMetadata

DECLARE @FinalMetadata TABLE
(
    MetadataId INT,
    MetadataTypeId INT,
    MetadataTypeValueId INT
)

INSERT INTO @FinalMetadata (MetadataId, MetadataTypeId, MetadataTypeValueId)
VALUES
(2,2,32),(8,2,35),(9,3,34),(10,4,1),(1,1,23)

--This is the same Type/TypeValue Id's from the table above
--However, they've been ID'd with existing IDs from the original table
--Or inserted and ID'd if they didn't exist
--Order doesn't matter for the result, I just need the IDs
SELECT * FROM @FinalMetadata

Tentative 1

J'ai d'abord essayé d'obtenir la valeur via une fusion en effectuant un insert dans la table des résultats lorsqu'il est apparié et en utilisant la sortie pour obtenir les nouvelles valeurs:

--Try to Insert the existing target when matched into the final table
MERGE @Metadata AS T
    USING @MergeMetadata AS S
        ON (S.MetadataTypeId = T.MetadataTypeId 
        AND S.MetadataTypeValueId = T.MetadataTypeValueId)
    WHEN MATCHED
        THEN --CANT INSERT WHEN MATCHED
            INSERT INTO (T.MetadataId, S.MetadataTypeId, S.MetadataTypeValueId) INTO @FinalMetadata
    WHEN NOT MATCHED BY TARGET
        THEN 
            INSERT (MetadataTypeId, MetadataTypeValueId) 
            VALUES (S.MetadataTypeId, S.MetadataTypeValueId)
    OUTPUT inserted.MetadataId, 
           inserted.MetadataTypeId, 
           inserted.MetadataValueTypeId,
           INTO @FinalMetadata
        ;

Tentative 2

J'ai ensuite essayé d'obtenir les identifiants d'origine à l'aide de la sortie - ne réalisais pas qu'il n'avait pas eu d'espace réservé pour mettre à jour :(

--Try to get the original IDs by OUTPUT
MERGE @Metadata AS T
    USING @MergeMetadata AS S
        ON (S.MetadataTypeId = T.MetadataTypeId 
        AND S.MetadataTypeValueId = T.MetadataTypeValueId)
    WHEN MATCHED
        THEN 
            UPDATE SET (T.MetadataId = T.MetadataId)
    WHEN NOT MATCHED BY TARGET
        THEN 
            INSERT (MetadataTypeId, MetadataTypeValueId) 
            VALUES (S.MetadataTypeId, S.MetadataTypeValueId)
    OUTPUT inserted.MetadataId, 
           updated.MetadataId, --updated isn't a key for OUTPUT 
           inserted.MetadataValueTypeId,
           INTO @FinalMetadata
        ;

Tentative 3

Enfin, j'ai tenté d'inverser mon plan d'attaque, mais je ne pouvais pas effectuer une insertion de la source

--Try to reverse logic and insert into Source when not matched
MERGE @MergeMetadata T
    USING @Metadata S
        ON (S.MetadataTypeId = T.MetadataTypeId 
        AND S.MetadataTypeValueId = T.MetadataTypeValueId)
    WHEN MATCHED
        THEN UPDATE SET T.MetadataId = S.MetadataId
    WHEN NOT MATCHED BY SOURCE
        THEN --can't insert in NOT MATCHED BY SOURCE
            INSERT (MetadataTypeId, MetadataTypeValueId) 
            VALUES (T.MetadataTypeId, T.MetadataTypeValueId) 
    ; 

Désolé pour la longueur, je voulais juste montrer que je lui ai donné une pensée et une tentative. À ce stade, devrais-je plutôt écrire le SELECT/SI NOT PAS exister/commencer à insérer la logique classique? De tout, je lisais de la fusion, cela semblait parfait pour cette situation, cependant, il ne semble pas vouloir me donner ce dont j'ai vraiment besoin.

Si je devrai suivre la voie d'une autre logique, pourriez-vous fournir un extrait de code recommandé qui gérerait la simultanéité Les exécutions simultanées n'exécuteraient pas la même chose n'existerait pas/insert en même temps?

7
tostringtheory

Les mises à jour sont suivies par la fusion en fournissant:

  • la valeur $ Action MISE À JOUR ;
  • les anciennes valeurs dans la table de mémoire supprimée; et
  • les nouvelles valeurs dans la table de mémoire inséré.

Par conséquent, dans le cas où une correspondance est trouvée, et dans le cas où il n'y a pas de correspondance par cible, les valeurs de la colonne d'identité sont disponibles dans la table de mémoire insérée.

3
Pieter Geerkens