|
|
|
Ten Centuries
      
Group: General Forum Members
Last Login: Saturday, January 19, 2013 8:28 AM
Points: 1,038,
Visits: 255
|
|
|
|
|
|
SSC Journeyman
      
Group: General Forum Members
Last Login: Thursday, May 16, 2013 3:19 AM
Points: 78,
Visits: 88
|
|
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
|
|
|
|
|
Ten Centuries
      
Group: General Forum Members
Last Login: Saturday, January 19, 2013 8:28 AM
Points: 1,038,
Visits: 255
|
|
You are correct. A correction is on it way.
Greg
Gregory A. Larsen, MVP
Need SQL Server Examples check out my website at http://www.sqlserverexamples.com
|
|
|
|
|
Forum Newbie
      
Group: General Forum Members
Last Login: Monday, May 13, 2013 1:37 AM
Points: 1,
Visits: 66
|
|
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 !
|
|
|
|
|
SSC Rookie
      
Group: General Forum Members
Last Login: Today @ 6:14 AM
Points: 27,
Visits: 125
|
|
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.
|
|
|
|
|
Forum Newbie
      
Group: General Forum Members
Last Login: Friday, May 10, 2013 10:46 AM
Points: 2,
Visits: 28
|
|
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
|
|
|
|
|
SSC-Addicted
      
Group: General Forum Members
Last Login: Sunday, May 12, 2013 4:01 AM
Points: 406,
Visits: 1,364
|
|
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?
|
|
|
|
|
Grasshopper
      
Group: General Forum Members
Last Login: Tuesday, May 14, 2013 8:08 AM
Points: 14,
Visits: 206
|
|
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.
|
|
|
|
|
Grasshopper
      
Group: General Forum Members
Last Login: Tuesday, May 14, 2013 8:08 AM
Points: 14,
Visits: 206
|
|
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
|
|
|
|
|
SSCommitted
      
Group: General Forum Members
Last Login: Tuesday, April 16, 2013 2:13 AM
Points: 1,505,
Visits: 226
|
|
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.
|
|
|
|