web-dev-qa-db-fra.com

Instruction SQL MERGE pour mettre à jour les données

J'ai une table avec des données nommées energydata

il n'a que trois colonnes

(webmeterID, DateTime, kWh)

J'ai un nouvel ensemble de données mises à jour dans une table temp_energydata.

La DateTime et la webmeterID restent les mêmes. Mais les valeurs kWh doivent être mises à jour à partir de la table temp_energydata.

Comment est-ce que j'écris le T-SQL pour ceci correctement?

53
user1745767

En supposant que vous souhaitiez obtenir une instruction SQL Server MERGE :

MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
    ON target.webmeterID = source.webmeterID
    AND target.DateTime = source.DateTime
WHEN MATCHED THEN 
    UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
    INSERT (webmeterID, DateTime, kWh)
    VALUES (source.webmeterID, source.DateTime, source.kWh);

Si vous souhaitez également supprimer des enregistrements de la cible qui ne figurent pas dans la source:

MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
    ON target.webmeterID = source.webmeterID
    AND target.DateTime = source.DateTime
WHEN MATCHED THEN 
    UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
    INSERT (webmeterID, DateTime, kWh)
    VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;

Parce que cela est devenu un peu plus populaire, je pense que je devrais développer un peu cette réponse avec quelques mises en garde à prendre en compte.

Premièrement, plusieurs blogs signalent des problèmes de simultanéité avec l'instruction MERGE dans des versions antérieures de SQL Server. Je ne sais pas si cette question a déjà été abordée dans les éditions ultérieures. Dans les deux cas, vous pouvez en grande partie contourner le problème en spécifiant l'indicateur de verrouillage HOLDLOCK ou SERIALIZABLE:

MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
[...]

Vous pouvez également accomplir la même chose avec des niveaux d'isolation de transaction plus restrictifs.

Il existe plusieurs autres problèmes connus avec MERGE. (Notez que depuis que Microsoft a nuancé Connect et n'a pas lié les problèmes de l'ancien système aux problèmes du nouveau système, ces problèmes plus anciens sont difficiles à dépister. Merci, Microsoft!) D'après ce que je peux dire, la plupart d'entre eux ne sont pas courants. problèmes ou peuvent être résolus avec les mêmes astuces de verrouillage que ci-dessus, mais je ne les ai pas testés.

Dans l’état actuel des choses, même si j’ai jamais eu de problèmes avec l’instruction MERGE, j’utilise toujours l’indicateur WITH (HOLDLOCK) à présent, et je préfère utiliser l’énoncé uniquement dans les cas les plus simples.

111
Bacon Bits

J'ai souvent utilisé Bacon Bits excellente réponse car je ne peux tout simplement pas mémoriser la syntaxe. 

Mais j’ajoute généralement un CTE comme complément pour rendre la partie DELETE plus utile car très souvent, vous souhaiterez appliquer la fusion uniquement à une partie de la table cible.

WITH target as (
    SELECT * FROM dbo.energydate WHERE DateTime > GETDATE()
)
MERGE INTO target WITH (HOLDLOCK)
USING dbo.temp_energydata AS source
    ON target.webmeterID = source.webmeterID
    AND target.DateTime = source.DateTime
WHEN MATCHED THEN 
    UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
    INSERT (webmeterID, DateTime, kWh)
    VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
13
Patrick Fromberg

Si vous avez simplement besoin de mettre à jour vos enregistrements dans energydata en vous basant sur les données de temp_energydata, en supposant que temp_enerydata ne contient aucun nouvel enregistrement, essayez ceci:

UPDATE e SET e.kWh = t.kWh
  FROM energydata e INNER JOIN 
       temp_energydata t ON e.webmeterID = t.webmeterID AND 
                            e.DateTime = t.DateTime

Ici fonctionne sqlfiddle

Mais si temp_energydata contient de nouveaux enregistrements et que vous devez l'insérer dans energydata, de préférence avec une déclaration, vous devez absolument choisir la réponse donnée par Bacon Bits.

4
peterm
UPDATE ed
SET ed.kWh = ted.kWh
FROM energydata ed
INNER JOIN temp_energydata ted ON ted.webmeterID = ed.webmeterID
0
Andrey Gordeev
Update energydata set energydata.kWh = temp.kWh 
where energydata.webmeterID = (select webmeterID from temp_energydata as temp) 
0
Azade