SQL Clone
SQLServerCentral is supported by Redgate
 
Log in  ::  Register  ::  Not logged in
 
 
 


Stairway to T-SQL DML Level 12: Using the MERGE Statement


Stairway to T-SQL DML Level 12: Using the MERGE Statement

Author
Message
Greg Larsen
Greg Larsen
SSCommitted
SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)

Group: General Forum Members
Points: 1655 Visits: 290
Comments posted to this topic are about the item Stairway to T-SQL DML Level 12: Using the MERGE Statement

Gregory A. Larsen, MVP
Ian Miles
Ian Miles
SSC-Enthusiastic
SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)SSC-Enthusiastic (102 reputation)

Group: General Forum Members
Points: 102 Visits: 118
An excellent primer on the MERGE statement, thank you.

Please can I point out what I believe is simply an inadvertent typo on your part? Re listing 4 you state:

"This clause tells SQL Server whenever it finds a record in the “Source” table that is not contained in the target table that it needs to perform a DELETE operation."

This should be the other way around i.e. when record is found in Target table that is not found in Source.

Great article, thanks again
Greg Larsen
Greg Larsen
SSCommitted
SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)SSCommitted (1.7K reputation)

Group: General Forum Members
Points: 1655 Visits: 290
You are correct. A correction is on it way.

Greg

Gregory A. Larsen, MVP
ankit_recsurat
ankit_recsurat
Grasshopper
Grasshopper (19 reputation)Grasshopper (19 reputation)Grasshopper (19 reputation)Grasshopper (19 reputation)Grasshopper (19 reputation)Grasshopper (19 reputation)Grasshopper (19 reputation)Grasshopper (19 reputation)

Group: General Forum Members
Points: 19 Visits: 159
Hi Greg,

Would like to extend my sincere thanks for such beautiful and elaborate articles on SQL Server.

I have a question hovering in my mind from past few months since I have read about MERGE. Can we use multiple conditions in 'ON' clause . For example :

MERGE dbo.Sales AS T -- Target
USING dbo.NewSalesNAdjustments AS S -- Source
ON T.Id = S.Id
AND T.SalesAmount = S.SalesAmount -- Can we use this condition too in 'ON' clause ??? I did try
--to test this but I wasn't getting correct results
WHEN MATCHED THEN -- Update
UPDATE SET T.SalesAmount = S.SalesAmount
WHEN NOT MATCHED THEN -- Insert
INSERT (Id,SalesAmount) VALUES (S.Id,S.SalesAmount);

Please Advise !
Vlad-207446
Vlad-207446
SSC Journeyman
SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)SSC Journeyman (80 reputation)

Group: General Forum Members
Points: 80 Visits: 321
you are most definitely can use AND/OR condition in the ON clause :-)
I have been using them forever...

also you can use an AND in MATCHED stmt.
Like so:
MERGE T
Using S
ON
t.col = s.col
WHEN MATCHED AND <otherTcol = otherScol condition> THEN
Update blah blah blah

but very cautiously
that part of MERGE is not always working as desired/expected.
paul.barbin
paul.barbin
Grasshopper
Grasshopper (17 reputation)Grasshopper (17 reputation)Grasshopper (17 reputation)Grasshopper (17 reputation)Grasshopper (17 reputation)Grasshopper (17 reputation)Grasshopper (17 reputation)Grasshopper (17 reputation)

Group: General Forum Members
Points: 17 Visits: 106
Curious to hear from others on whether they use the MERGE command in production and what it's performance is like.

We began using it in our unit test scripts to build data for developers immediately following the database build (we're using SSDT 2012). We wanted to use MERGE so the scripts could run even if developers already had data in their database.

So, rather than having an insert script of 10,000 records, we use the Merge command along with the 10,000 rows using the VALUES statement. This is EXTREMELY slow!
It takes 30 minutes to populate a small database (5GB) vs 2-3 minutes using TRUNCATE/INSERT.

Paul
R.P.Rozema
R.P.Rozema
Ten Centuries
Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)Ten Centuries (1.1K reputation)

Group: General Forum Members
Points: 1097 Visits: 1685
Excellent article indeed. However like in most articles on merge, you do not mention the fact that merge doesn't have a where clause. i.e. if you want to use only a part of a table as the merge target because your source data set only represents a subset of the data included in the entire table, without precautions the merge statement will delete all rows that are not in your subset. As an example scenario: you've just received one supplier's list of products and want to merge this into your dbo.products table, that contains the list of products for all of your suppliers. The fully implemented merge statement will correctly insert all missing products for your supplier and also it will correctly update all products for which the supplier has changed information and it will even remove all products from your table that are no longer in the supplier's product list. BUT, it will also delete all products from your table that are from other suppliers. Most likely not what you intended! A where clause would have been very welcome here.

There are 2 ways around this:
1 - extend the source table for the merge such that it includes all existing products entries for all but the selected supplier plus the new list of products for the selected supplier, or
2 - apply some filtering on the target table.
Obviously method 1 will result in an unnecessary large amount of rows being processed by the merge statement. It can be implemented using for example a union statement, but I won't demonstrate this here. Method 2 does at first sight not seem possible because merge doesn't have a where clause. But it is still possible by using a common table expression as the target. Here's an example:
use tempdb
go

create table dbo.suppliers (
supplier_ID int not null,
supplier_name varchar(200) not null,
constraint pk_suppliers primary key clustered (supplier_id)
);

create table dbo.supplierproducts (
supplier_id int not null,
product_code varchar(20) not null,
product_name varchar(200) not null,
constraint pk_products primary key clustered (supplier_id, product_code),
constraint fk_products_supplier foreign key (supplier_id) references dbo.suppliers (supplier_id) on delete cascade on update cascade not for replication
);

insert dbo.suppliers( supplier_id, supplier_name)
values (1, 'supplier one'),
(2, 'supplier two'),
(3, 'supplier three');
go

insert dbo.supplierproducts( supplier_id, product_code, product_name)
values (1, 'apple', 'red apples'),
(1, 'pear', 'red pears'),
(1, 'rose', 'red roses'),
(2, 'apple', 'white apples'),
(2, 'pear', 'white pears'),
(3, 'apple', 'blue apples'),
(3, 'pear', 'blue pears');
go

-- Show the content of supplierproducts table before the merge.
select * from dbo.supplierproducts
go


-- Here's the new product list supplier 1 has just sent us.
-- Notice that this list only includes supplier 1's products,
-- not the products of any other suppliers.
declare @updatedproductlist table (
product_code varchar(20) not null,
product_name varchar(200) not null,
primary key (product_code)
);

insert @updatedproductlist( product_code, product_name)
values ('apple', 'red apples'),
('pear', 'green pears'),
('kiwi', 'green kiwis');


declare @supplier_id int;

select @supplier_id = 1;

with cteTarget as (
select sp.supplier_id,
sp.product_code,
sp.product_name
from dbo.supplierproducts sp
where sp.supplier_id = @supplier_id
)
merge into cteTarget trg
using @updatedproductlist src
on (src.product_code = trg.product_code)
when not matched by target
then
insert( supplier_id, product_code, product_name)
values( @supplier_id, src.product_code, src.product_name)

when matched and src.product_name <> trg.product_name
then
update
set product_name = src.product_name
when not matched by source
then
delete
output $action, inserted.*, deleted.*;


-- Show the content of supplierproducts table before the merge.
-- Note that the products of supplier 2 and 3 are unchanged.
select * from dbo.supplierproducts;





Posting Data Etiquette - Jeff Moden
Posting Performance Based Questions - Gail Shaw
Hidden RBAR - Jeff Moden
Cross Tabs and Pivots - Jeff Moden
Catch-all queries - Gail Shaw


If you don't have time to do it right, when will you have time to do it over?
Ryan.Polk
Ryan.Polk
SSC Journeyman
SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)

Group: General Forum Members
Points: 93 Visits: 574
paul.barbin (1/16/2013)
Curious to hear from others on whether they use the MERGE command in production and what it's performance is like.

We began using it in our unit test scripts to build data for developers immediately following the database build (we're using SSDT 2012). We wanted to use MERGE so the scripts could run even if developers already had data in their database.

So, rather than having an insert script of 10,000 records, we use the Merge command along with the 10,000 rows using the VALUES statement. This is EXTREMELY slow!
It takes 30 minutes to populate a small database (5GB) vs 2-3 minutes using TRUNCATE/INSERT.

Paul


Have you checked out MS's article (link) for MERGE optimization?

The biggest takeaway:

To improve the performance of the MERGE statement, we recommend the following index guidelines:

Create an index on the join columns in the source table that is unique and covering.

Create a unique clustered index on the join columns in the target table.


I've noticed slow performance in MERGE, but never when following these guidlines - which sometimes forces me into staging tables, but the MERGE benefits have outweighed the downsides there.
Ryan.Polk
Ryan.Polk
SSC Journeyman
SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)SSC Journeyman (93 reputation)

Group: General Forum Members
Points: 93 Visits: 574
R.P.Rozema (1/16/2013)
Excellent article indeed. However like in most articles on merge, you do not mention the fact that merge doesn't have a where clause. i.e. if you want to use only a part of a table as the merge target because your source data set only represents a subset of the data included in the entire table, without precautions the merge statement will delete all rows that are not in your subset.


This is easily preventable by extending the "WHEN NOT MATCHED BY SOURCE" clause, ie:

WHEN NOT MATCHED BY SOURCE AND T.TypeID = 1


RuiMauricio
RuiMauricio
SSCommitted
SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)SSCommitted (1.6K reputation)

Group: General Forum Members
Points: 1563 Visits: 291
In reply to paul.barbin

I have had exactly the opposite experience and i like the syntax of it.

We used MERGE, EXCEPT and INTERSECT in a few ad-hoc queries and sproc ammendments with excellent results. The last time we used merge the operation performed nearly 50 million inserts in a matter of minutes.
Go


Permissions

You can't post new topics.
You can't post topic replies.
You can't post new polls.
You can't post replies to polls.
You can't edit your own topics.
You can't delete your own topics.
You can't edit other topics.
You can't delete other topics.
You can't edit your own posts.
You can't edit other posts.
You can't delete your own posts.
You can't delete other posts.
You can't post events.
You can't edit your own events.
You can't edit other events.
You can't delete your own events.
You can't delete other events.
You can't send private messages.
You can't send emails.
You can read topics.
You can't vote in polls.
You can't upload attachments.
You can download attachments.
You can't post HTML code.
You can't edit HTML code.
You can't post IFCode.
You can't post JavaScript.
You can post emoticons.
You can't post or upload images.

Select a forum

































































































































































SQLServerCentral


Search