DataXperts
This new command is extremely useful for being a one stop construct to handle what Oracle or Informatica commonly call Upserts. Of course you can get by with the old is exists logic and programatically handle the inserts and updates. That is until you get into a serious Data Warehouse load. Lots of rows of data where some will already exist in your fact tables, and others will be new inserts. How do you track the keys, whether existing or a new identity insert, to have on hand wh3en you pupulate the child dimensions? The Merge command give you an Output clause to expose that ID (or any other column) and whether it was inserted or updated!
CREATE TABLE Employee
(
Employee_ID INT IDENTITY,
Employee_first_name nvarchar(50),
Employee_last_name nvarchar(50),
Employee_email varchar(100),
Employee_hire_date datetime,
Employee_birth_date DATETIME,
Last_Updated DATETIME DEFAULT GETDATE()
Use the Code Snippet below to enter a couple records.
Run it once as is.
Then change @p_Employee_first_name to ‘employee2’ and run it again.
Notice when you run it, you will get output showing you whether it inserted or
updated and the field values.
You should have two records, employee1 and employee2 at this point.
Now change @p_Employee_first_name back to ‘employee1’ and also change @p_Employee_email to ‘newemail@test.com’ and run it again.
When you run it this time, the Merge construct will find a match and execute the “WHEN MATCHED” section of code instead of the “WHEN NOT MATCHED” code. The output results will also show that it found a match. The $action will show UPDATE, and the record had the Employee_email and Last_Updated fields updated.
Declare @p_Employee_first_name nvarchar(50),
@p_Employee_last_name nvarchar(50),
@p_Employee_email varchar(100) =null,
@p_Employee_hire_date datetime =null,
@p_Employee_birth_date datetime =null
set @p_Employee_first_name = 'employee1'
set @p_Employee_last_name = 'whiteside'
set @p_Employee_email = 'email@test.com'
set @p_Employee_hire_date = '01/01/2007'
set @p_Employee_birth_date = '06/01/1970'
MERGE INTO dbo.Employee AS T
USING
(Select
@p_Employee_first_name, @p_Employee_last_name) AS S
(Employee_first_name, Employee_last_name)
ON T.Employee_first_name = S.Employee_first_name and T.Employee_last_name = S.Employee_last_name
WHEN MATCHED
THEN UPDATE SET T.last_updated = GETDATE(),
T.Employee_email = @p_Employee_Email
WHEN NOT MATCHED
THEN
INSERT
(
[Employee_first_name]
,[Employee_last_name]
,[Employee_email]
,[Employee_hire_date]
,[Employee_birth_date]
)
VALUES
(
@p_Employee_first_name
,@p_Employee_last_name
,@p_Employee_email
,@p_Employee_hire_date
,@p_Employee_birth_date
)
Output $action, inserted.*;
Let’s modify the Code Snippet just a bit to track the keys.
Run the Code Snippet below once as is, this will create ‘employee3’ for a third record
Now change @p_Employee_first_name to “employee1” and @p_Employee_email to “update@hotmail.com” . Then run the script for the first Declare down.
We can now see the stored results of what happened inside the hash table
Select * from #Employee
And of course the values in the Destination Fact Table with Select * from Employee
Create table #Employee
(
Employee_ID INT,
Employee_first_name nvarchar(50),
Employee_last_name nvarchar(50),
Employee_email varchar(100),
Employee_hire_date datetime,
Employee_birth_date DATETIME,
Last_Updated DATETIME
)
Declare @p_Employee_first_name nvarchar(50),
@p_Employee_last_name nvarchar(50),
@p_Employee_email varchar(100) =null,
@p_Employee_hire_date datetime =null,
@p_Employee_birth_date datetime =null
set @p_Employee_first_name = 'employee3'
set @p_Employee_last_name = 'whiteside'
set @p_Employee_email = 'email@test.com'
set @p_Employee_hire_date = '01/01/2007'
set @p_Employee_birth_date = '06/01/1970'
MERGE INTO dbo.Employee AS T
USING
(Select @p_Employee_first_name, @p_Employee_last_name) AS S (Employee_first_name, Employee_last_name)
ON T.Employee_first_name = S.Employee_first_name and T.Employee_last_name = S.Employee_last_name
WHEN MATCHED
THEN UPDATE SET T.last_updated = GETDATE(),
T.Employee_email = @p_Employee_Email
WHEN NOT MATCHED
THEN
INSERT
(
[Employee_first_name]
,[Employee_last_name]
,[Employee_email]
,[Employee_hire_date]
,[Employee_birth_date]
)
VALUES
(
@p_Employee_first_name
,@p_Employee_last_name
,@p_Employee_email
,@p_Employee_hire_date
,@p_Employee_birth_date
)
OUTPUT INSERTED.Employee_ID AS Employee_ID,
INSERTED.Employee_first_name AS Employee_first_name,
INSERTED.Employee_last_name AS Employee_last_name,
INSERTED.Employee_email AS Employee_email,
INSERTED.Employee_hire_date AS Employee_hire_date,
INSERTED.Employee_birth_date AS Employee_birth_date,
INSERTED.Last_Updated AS Last_Updated
into #Employee;
Now imagine if you were loading data from one warehouse into another. Keys from the source exist to establish the surrogate relationships, but you cannot use those keys in the target Data Warehouse. The source relationships are already set in place mean nothing to the human eye, but you need to preserve that relationship, but must utilize new keys. You could easily add the OldKey to the #Employee table and store that value from the source data into it for each row. Now if you need to join back to more dimensions from the source data, you have the old key to establish that relationship. Simply join the #Employee table (with the OldKey inserted) to the next source dimension by the OldKey to bring over the Dimensions as they were setup from the original data, but with new keys being established.
If you are certain the Source data is all new data, you can bypass the match / not match logic by changing the code with
ON
(1=0) –force a not match condition
WHEN NOT MATCHED
THEN
And still utilize the OUTPUT clause to track the Key values.