Continuing, or should I say, beginning, our look at SQL Server 2008, I will be spending the next few posts discussing new and improved features in T-SQL.
Let’s start by examining the MERGE statement.
What does the MERGE statement let you do? The MERGE statement will allow you to Insert, Update, and Delete rows in your target table in just a single statement. Before the MERGE statement you would have to do each operation separately using complex join statements or WHERE clauses.
Syntax:
(from SQL 2008 Books Online)
[ WITH <common_table_expression> [,...n] ] MERGE [ TOP ( expression ) [ PERCENT ] ] [ INTO ] target_table [ [ AS ] table_alias ] [ WITH ( <merge_hint> ) ] USING <table_source> ON <search_condition> [ WHEN MATCHED [ AND <search_condition> ] THEN <merge_matched> ] [ WHEN [TARGET] NOT MATCHED [ AND <search_condition> ] THEN <merge_not_matched> ] [ WHEN SOURCE NOT MATCHED [ AND <search_condition> ] THEN <merge_ matched> ] <output_clause> [ OPTION ( <query_hint> [ ,...n ] ) ]; <merge_hint>::= { [ <table_hint_limited> [ ,...n ] ] [ [ , ] INDEX ( index_val [ ,...n ] ) ] } <table_source> ::= { table_or_view_name [ [ AS ] table_alias ] [ <tablesample_clause> ] [ WITH ( table_hint [ [ , ]...n ] ) ] | rowset_function [ [ AS ] table_alias ] [ ( bulk_column_alias [ ,...n ] ) ] | user_defined_function [ [ AS ] table_alias ] | OPENXML <openxml_clause> | derived_table [ AS ] table_alias [ ( column_alias [ ,...n ] ) ] | <joined_table> | <pivoted_table> | <unpivoted_table> } <merge_matched>::= { UPDATE SET <set_clause> | DELETE } <set_clause>::= { column_name = { expression | DEFAULT | NULL } | { udt_column_name. { { property_name = expression | field_name = expression } | method_name ( argument [ ,...n ] ) } } | column_name { .WRITE ( expression , @Offset , @Length ) } | @variable = expression } [ ,...n ] <merge_not_matched>::= INSERT [ ( <column_list> ) ] { VALUES ( <values_list> ) | DEFAULT VALUES } <output_clause>::= { [ OUTPUT <dml_select_list> INTO { @table_variable | output_table } [ (column_list) ] ] [ OUTPUT <dml_select_list> ] } <dml_select_list>::= { <column_name> | scalar_expression } [ [AS] column_alias_identifier ] [ ,...n ] <column_name> ::= { DELETED | INSERTED | from_table_name } . { * | column_name } | $ACTION
Example:
Scenario: You regularly update a table from a bulk source. The incoming dataset has new records and updates to existing records.
You maintain a table of the current inventory, which is updated nightly to reflect the transactions from the day.
Assumptions: Products are kept in a Product table which is not shown below. A foreign-key relationship exists with the Inventory table. The LastModified field is automatically populated by a default for all inserts.
DECLARE @InventoryMaster TABLE(InventoryID INT IDENTITY, ProductID INT, QtyOnHand INT, ReorderLevel INT, LastModified datetime DEFAULT GETDATE() ); DECLARE @DailyTransactions TABLE(ProductID INT, TransQty INT); INSERT INTO @InventoryMaster (ProductID, QtyOnHand, ReorderLevel) VALUES (1, 100, 20), (2, 50, 10), (3, 1000, 100), (25, 200, 50), (26, 200, 20); INSERT INTO @DailyTransactions (ProductID, TransQty) VALUES (1, -20), (1, 5), (3, -25), (3, 10), (25, -5), (24, 10), (25, 50); SELECT * FROM @InventoryMaster; MERGE @InventoryMaster inv USING (SELECT ProductID, SUM(TransQty) as TransQty FROM @DailyTransactions GROUP BY ProductID) dt ON inv.ProductID = dt.ProductID WHEN MATCHED THEN UPDATE SET QtyOnHand += TransQty WHEN NOT MATCHED THEN INSERT VALUES(ProductID, TransQty, TransQty*.25,GetDate()) OUTPUT $action,inserted.*,deleted.*; SELECT * FROM @InventoryMaster;