{"id":215,"date":"2011-06-06T00:00:17","date_gmt":"2011-06-06T00:00:17","guid":{"rendered":"http:\/\/www.sqlserver.fr\/blog\/?p=215"},"modified":"2026-05-02T14:31:19","modified_gmt":"2026-05-02T12:31:19","slug":"merge","status":"publish","type":"post","link":"https:\/\/www.sqlserver.fr\/blog\/merge\/","title":{"rendered":"Merge"},"content":{"rendered":"<p>Avec SQL Server 2008, la triplette d\u2019instructions INSERT \/ UPDATE \/ DELETE s\u2019est vu adjoindre une instruction compl\u00e9mentaire nomm\u00e9e MERGE. Voici une br\u00e8ve pr\u00e9sentation de cette nouvelle commande.<!--more-->L\u2019instruction MERGE est pr\u00e9sent\u00e9e dans l\u2019aide en ligne \u00e0 cette page : <a href=\"https:\/\/docs.microsoft.com\/fr-fr\/sql\/t-sql\/statements\/merge-transact-sql\">https:\/\/docs.microsoft.com\/fr-fr\/sql\/t-sql\/statements\/merge-transact-sql<\/a>. La syntaxe est un peu herm\u00e9tique au premier abord, mais cette commande se r\u00e9v\u00e8le \u00e0 l\u2019usage assez puissante et surtout tr\u00e8s performante.<br \/>\nEn m\u2019appuyant sur certains des exemples pr\u00e9sent\u00e9s dans l\u2019aide en ligne et sur la base de donn\u00e9es exemple (voir <a href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/tag\/adventureworks\">https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/tag\/adventureworks<\/a>), je vais d\u00e9tailler quelque peu le premier exemple pr\u00e9sent dans l\u2019aide en ligne.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">USE AdventureWorks2008R2;\r\nGO\r\nIF OBJECT_ID (N'Production.usp_UpdateInventory', N'P') IS NOT NULL DROP PROCEDURE Production.usp_UpdateInventory;\r\nGO\r\nCREATE PROCEDURE Production.usp_UpdateInventory\r\n@OrderDate datetime\r\nAS\r\nMERGE Production.ProductInventory AS target\r\nUSING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod\r\n\t\tJOIN Sales.SalesOrderHeader AS soh\r\n\t\t\tON sod.SalesOrderID = soh.SalesOrderID\r\n\t\t\tAND soh.OrderDate = @OrderDate\r\n\t\tGROUP BY ProductID) AS source (ProductID, OrderQty)\r\nON (target.ProductID = source.ProductID)\r\nWHEN MATCHED AND target.Quantity - source.OrderQty &lt;= 0\r\n\tTHEN DELETE\r\nWHEN MATCHED\r\n\tTHEN UPDATE SET target.Quantity = target.Quantity - source.OrderQty,\r\n\t\t\t\t\ttarget.ModifiedDate = GETDATE()\r\n\t\t\t\tOUTPUT $action, Inserted.ProductID, Inserted.Quantity, Inserted.ModifiedDate, Deleted.ProductID,Deleted.Quantity, Deleted.ModifiedDate;\r\nGO\r\n\r\nEXECUTE Production.usp_UpdateInventory '20030501'<\/pre>\n<p>D\u00e9composons la syntaxe MERGE.<br \/>\nPremi\u00e8re partie : le mot-cl\u00e9 MERGE lui-m\u00eame. Il s\u2019agit l\u00e0 de pr\u00e9ciser la table cible, c\u2019est-\u00e0-dire celle qui sera mise \u00e0 jour. Dans notre cas, il s\u2019agit de la table Production.ProductionInventory, et l\u2019alias \u2018target\u2019 lui est affect\u00e9. A noter que \u2018target\u2019 n\u2019est ici qu\u2019un simple alias, le fait qu\u2019il soit \u00e9crit dans une police bleue n\u2019a rien \u00e0 voir avec la syntaxe MERGE elle-m\u00eame, c\u2019est juste une simple co\u00efncidence, \u2018target\u2019 est en fait un mot-cl\u00e9 li\u00e9 \u00e0 l\u2019instruction CREATE CONTRACT (voir <a href=\"https:\/\/docs.microsoft.com\/fr-fr\/sql\/t-sql\/statements\/create-contract-transact-sql\">https:\/\/docs.microsoft.com\/fr-fr\/sql\/t-sql\/statements\/create-contract-transact-sql<\/a> ).<br \/>\nEnsuite, nous avons les donn\u00e9es sources, pr\u00e9sent\u00e9es sous forme d\u2019un jeu de r\u00e9sultats (commen\u00e7ant donc par le mot-cl\u00e9 SELECT), introduites par USING. L\u2019alias \u2018source\u2019 est ici associ\u00e9 \u00e0 ce jeu de r\u00e9sultat (l\u00e0 encore, il ne s\u2019agit pas du tout d\u2019un mot cl\u00e9), avec nommage des colonnes du jeu de r\u00e9sultat.<br \/>\nL\u2019instruction d\u00e9finit ensuite la clause de jointure (mot-cl\u00e9 ON) entre la table cible et le jeu de r\u00e9sultat source. Il est \u00e0 noter qu\u2019il ne devra pas y avoir d\u2019ambigu\u00eft\u00e9 sur l\u2019action \u00e0 appliquer \u00e0 chacune des lignes de la table cible, et que chaque ligne ne peut faire l\u2019objet que d\u2019une action au maximum. Notamment, la clause ON doit \u00eatre assez pr\u00e9cise pour \u00e9viter des doublons, et l\u2019instruction MERGE ne peut notamment pas servir \u00e0 agr\u00e9ger dans la table cible les diff\u00e9rentes lignes du jeu de r\u00e9sultat source. Si le besoin consiste \u00e0 agr\u00e9ger des actions vers une table cible, c\u2019est directement au sein du jeu de r\u00e9sultats source que doivent \u00eatre agr\u00e9g\u00e9es les donn\u00e9es (et c\u2019est d\u2019ailleurs le cas ici, puisque la sommation est faite \u00e0 l\u2019int\u00e9rieur du SELECT consomm\u00e9 par USING).<br \/>\nEnfin, le plus int\u00e9ressant, le listing des diff\u00e9rentes actions. Et en combinant d\u2019une part les cas de correspondances entre la table cible et le jeu de r\u00e9sultat source, ou ceux des donn\u00e9es pr\u00e9sentes uniquement d\u2019un des deux c\u00f4t\u00e9s (source ou cible), et d\u2019autre part toutes les possibilit\u00e9s de conditions que l\u2019on peut ajouter (en consommant les colonnes de la table cible et \/ ou du jeu de r\u00e9sultat source), le champ des possibilit\u00e9s devient immense. Dans notre exemple, il y a par exemple une comparaison entre les quantit\u00e9s de la source et de la cible.<br \/>\nChaque condition WHEN entra\u00eene une action dans la table cible qui peut \u00eatre un ajout (INSERT), une suppression (DELETE) ou une mise \u00e0 jour (UPDATE). L\u2019ordre des clauses WHEN a une importance certaine, car d\u00e8s lors qu\u2019une association source \/ cible a v\u00e9rifi\u00e9 l\u2019une des clauses, le traitement de cette association s\u2019arr\u00eate l\u00e0. Ainsi, dans l\u2019exemple, l\u2019action UPDATE de la deuxi\u00e8me clause WHEN n\u2019est ex\u00e9cut\u00e9e que sur les enregistrements n\u2019ayant pas \u00e9t\u00e9 trait\u00e9s par la premi\u00e8re clause WHEN.<br \/>\nEt la force de l\u2019instruction MERGE r\u00e9side surtout dans le fait que la table cible n\u2019est parcourue qu\u2019une seule fois. Ainsi, l\u00e0 o\u00f9, sans cette instruction, il y aurait besoin d\u2019un parcours pour tester les conditions, puis d\u2019un autre pour la premi\u00e8re action, et enfin d\u2019une autre pour la deuxi\u00e8me action.<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2012\/03\/Plan.png\"><img loading=\"lazy\" decoding=\"async\" title=\"Plan\" src=\"https:\/\/www.sqlserver.fr\/blog\/wp-content\/uploads\/2012\/03\/Plan-1024x213.png\" alt=\"\" width=\"620\" height=\"128\" \/><\/a><\/p>\n<p>Pour terminer l\u2019instruction (juste avant son point-virgule obligatoire), la clause Output (pr\u00e9sente depuis SQL Server 2005), nous donne acc\u00e8s aux tables virtuelles inserted et deleted, compl\u00e9t\u00e9es d\u2019une colonne $action qui permet de pr\u00e9ciser l\u2019action appliqu\u00e9e sur chaque ligne impact\u00e9e dans la table cible.<br \/>\nN\u2019h\u00e9sitez pas \u00e0 me contacter si vous avez des questions par rapport \u00e0 cette instruction, c\u2019est l\u2019une de mes pr\u00e9f\u00e9r\u00e9es sous SQL Server 2008.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Avec SQL Server 2008, la triplette d\u2019instructions INSERT \/ UPDATE \/ DELETE s\u2019est vu adjoindre une instruction compl\u00e9mentaire nomm\u00e9e MERGE. Voici une br\u00e8ve pr\u00e9sentation de cette nouvelle commande.<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-215","post","type-post","status-publish","format-standard","hentry","category-article_sql"],"_links":{"self":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/215","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/comments?post=215"}],"version-history":[{"count":11,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/215\/revisions"}],"predecessor-version":[{"id":1962,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/215\/revisions\/1962"}],"wp:attachment":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/media?parent=215"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/categories?post=215"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/tags?post=215"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}