Using multiple statements in WHEN MATCHED - sql

I am using MERGE statement in order to insert XML input to SQL Server database table. How to execute multiple conditions in WHEN MATCHED block. Please refer the below code.
USING TableRelationship AS new
ON (new.TableRelationshipTypeID = old.TableRelationshipTypeID) AND old.ToRoleID = #RoleID
WHEN MATCHED THEN
UPDATE
SET old.FromRoleID = new.FromRoleID
-- Condition 2
-- Condition 3
Currently WHEN MATCHED it only executes this old.FromRoleID = new.FromRoleID line. How can I execute all 3 lines (-- Condition 2 and 3) inside WHEN NOT MATCHED condition.
Ex :
This is what I expect. WHEN MATCHED I just want to update the old field (old.ThruDate = GETDATE()) and insert a record to the same table. I cant separate those statements by a comma. SQL emits
Incorrect Syntax
MERGE INTO Party.TableRelationship AS old
USING TableRelationship AS new ON (new.TableRelationshipTypeID = old.TableRelationshipTypeID) AND old.ToRoleID = #RoleID
WHEN MATCHED THEN
UPDATE
SET old.ThruDate = GETDATE(),
INSERT (FromRoleID, ToRoleID, TableRelationshipTypeID)
VALUES (new.FromRoleID, new.ToRoleID, new.TableRelationshipTypeID);
Thank you.

You could use INSERT over DML to achieve it:
INSERT INTO tab_name(FromRoleID, ToRoleID, TableRelationshipTypeID)
SELECT FromRoleID, ToRoleID, TableRelationshipTypeID
FROM (
MERGE INTO Party.TableRelationship AS old
USING TableRelationship AS new
ON new.TableRelationshipTypeID = old.TableRelationshipTypeID
AND old.ToRoleID = #RoleID
WHEN MATCHED THEN
UPDATE SET old.ThruDate = GETDATE()
OUTPUT $action, FromRoleID, ToRoleID, TableRelationshipTypeID
) sub(action, FromRoleID, ToRoleID, TableRelationshipTypeID)
WHERE action = 'UPDATE';
Keep in mind that this method has some limitations more info:
MS Connect

You cannot do that - it is just plain not supported by SQL Server's MERGE statement.
In the WHEN MATCHED case, you can only do one UPDATE (or DELETE) statement - you cannot have multiple different statements after one another.
Check the official MSDN documentation for MERGE - it spells out what's possible and what's not supported in great detail:
Syntax:
MERGE
USING <table_source> ON <merge_search_condition>
[ WHEN MATCHED [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
;
<merge_matched>::=
{ UPDATE SET <set_clause> | DELETE }
The MERGE statement can have at most two WHEN MATCHED clauses. If two
clauses are specified, then the first clause must be accompanied by an
AND clause. For any given row, the second WHEN
MATCHED clause is only applied if the first is not. If there are two
WHEN MATCHED clauses, then one must specify an UPDATE action and one
must specify a DELETE action

This isn't what a merge statement is for. A merge statement either updates records if they already exist (WHEN MATCHED THEN), or it inserts records if they don't exist (WHEN NOT MATCHED THEN). It's not designed to insert records upon finding a match
(Side note: sqlserver merge statements can specify WHEN MATCHED twice, perhaps you'd use this with the aim that one of the matchings deletes records from the target that match some additional criteria, and the other specifies a list of columns to update if the additional logical test is false)
If you want to mix and match your inserting and updating when a record is matched, you'll need to use something else like a stored procedure or trigger that reacts to the update
Alternatively if you're just asking about how syntax of a merge statement, your insert directives need to be preceded by WHEN NOT MATCHED THEN INSERT ...

Those are not conditions, they are assignment clauses. You separate them by commas, as in update:
USING TableRelationship AS new
ON (new.TableRelationshipTypeID = old.TableRelationshipTypeID) AND
old.ToRoleID = #RoleID
WHEN MATCHED THEN UPDATE
SET old.FromRoleID = new.FromRoleID,
<assignment 2>,
<assignment 3>;

Related

Execute result from subquery in a merge into statement

I have a query that has as the output the following table (2 columns). What it means, is that I'm going to use the result to wrap it inside a merge into statement.
INSERT_COLUMNS UPDATE_COLUMNS
BANK_NAME target.BANK_NAME = source.BANKNAME
What I'm talking about is this:
with sql_prepare_merge as (
SELECT *
FROM another_table
),
MERGE INTO bank_raw AS target
USING bank AS source
ON source.id = target.id
WHEN MATCHED THEN
UPDATE SET (select update_columns from sql_prepare_merge)
WHEN NOT MATCHED THEN
INSERT (select insert_columns from sql_prepare_merge)
VALUES (source.id, (select insert_columns from sql_prepare_merge));
Keep in mind that the "sql_prepare_merge" is the name from the CTE where I'm getting the table I shared with you - and it has much more code in it, but they don't help here. So, I'm planning to take the text resulting from the subquery and insert it inside the merge statement.
So far, the error I'm getting is: syntax error line 19 at position 4 unexpected 'WHEN'.. By the way, this is inside Snowflake.
Unfortunately this is not possible as per my understanding of the docs: https://docs.snowflake.com/en/sql-reference/sql/merge.html#notmatchedclause-for-inserts
In the non matched clause for inserts you can specify only such values/expressions, refer to the source relations. This means you somehow need to adjust your source part itself (e.g. by joining the initial source with another_table). If this is not possible, you would need to go for separate INSERT and UPDATE statements.

WHERE Statement in MERGE

Can someone explain how will work WHERE at the end of the MERGE statement if it has conditions on a target and a source table? For instance:
merge into target_table t
using source source_table s
on s.flield = t.field
when matched then update (...)
when not matched then insert (...)
where t.field != <value> and s.field != <value>
I can't get how will be resolved t.field because of the source strings are matching with no one string of target table in the when not matched clause.
I did some tests so it seems to me rows will be never inserted.
I want to know: any row will be inserted or not?
From the official Oracle documentation for MERGE statement:
Specify the where_clause if you want the database to execute the update operation only if the specified condition is true. The condition can refer to either the data source or the target table. If the condition is not true, then the database skips the update operation when merging the row into the table.
To answer your question explicitly ("I want to know: any row will be inserted or not?"), the anwer is: NO.
Why? Because you have the wrong syntax.
You're using target table column (t.field) inside the merge_insert_clause, which is not allowed, and it should raise the ORA-38102: Invalid column in the INSERT WHERE Clause error: see this fiddle example!
From the docs:
The merge_insert_clause specifies values to insert into the column of the target table if the condition of the ON clause is false.
If you describe more precisely when do you want to insert and when do you want to update than I'll also edit my answer with further instruction how to do so.
I hope I helped!

Is Oracle MERGE NOT MATCHED THEN UPDATE possible?

We'd like to set the IS_DEL bit = 1 when a record exists in a Target table that doesn't exist in the Source table.
Is it possible to use a MERGE statement using the WHEN NOT MATCHED clause, but make it perform an UPDATE?
When attempting to do so, I'm getting a "ORA-00905: missing keyword" message.
MERGE
INTO AMEPSA.ENTERPRISE_LOCATION trg
USING (
SELECT C.LOCATION_KEY as LOCATION_KEY
FROM AMEPSA.ENTERPRISE_LOCATION C
INNER JOIN AMESTAGE.VW_LOCATION L ON C.REC_SRC_KEY_CD = L.LOCATION_ID
WHERE C.CURR_REC_IND = 'Y'
) src
ON (trg.LOCATION_KEY = src.LOCATION_KEY)
WHEN NOT MATCHED THEN UPDATE
SET trg.IS_DEL = 1
Does the "WHEN NOT MATCH" clause only support "THEN INSERT"?
From the documentation:
Use the MERGE statement to select rows from one or more sources for update or insertion into a table or view. You can specify conditions to determine whether to update or insert into the target table or view.
The syntax looks for rows in the source table (src) which do or do not have matching rows in the target table (trg). If there is a matching target row then it updates that; if there is not a matching row then it inserts a new row in the target table.
It does not, and cannot, look for rows in the target table that are not matched in the source table - which is what you are trying to identify and update.
The syntax diagrams for WHEN MATCHED and WHEN NOT MATCHED also make it clear that you cannot do WHEN NOT MATCHED THEN UPDATE.
Yes you can only insert when not match. See exact options in oracle merge.
The condition can refer to either the data source or the target table. If the condition is not true, then the database skips the update operation when merging the row into the table.

(T)SQL Merge Command

I want to use the merge command to update all rows in the target which are not in the source set.
I only use two operations with an update command, but I always get "Can not insert duplicate key errors" - as I read the technet article there should not be any insert statement executed...
declare #stagingid as decimal = 2
MERGE SDH.dbo.SDH_ProduktPreis AS T
USING (SELECT *
FROM Staging.mtc.SDH_Produktpreis
WHERE Staging_ID = #stagingid) AS S
ON S.PRPR_CPC = T.PRPR_CPC
AND S.PRPR_Preistyp = T.PRPR_Preistyp
AND T.PRPR_Origin = 'FromSource'
WHEN MATCHED
AND S.PRPR_CPC = T.PRPR_CPC
AND S.PRPR_Preistyp = T.PRPR_Preistyp
AND S.PRPR_Preis <> T.PRPR_Preis
AND S.PRPR_Gueltig_bis = T.PRPR_Gueltig_bis
AND S.Staging_ID = #stagingid
THEN UPDATE SET T.PRPR_sys_status = 6,
T.PRPR_Gueltig_bis = (SELECT CAST(GETDATE() as datetime)),
T.PRPR_sys_change_timestamp = GETDATE()
WHEN NOT MATCHED BY SOURCE
AND T.PRPR_Origin= 'FromSource'
THEN UPDATE SET T.PRPR_Gueltig_bis = (SELECT CAST(GETDATE() as datetime)),
T.PRPR_sys_status = 6,
T.PRPR_sys_change_timestamp = GETDATE();
If I add the "WHEN NOT MATCHED BY SOURCE" command SSMS always complain about inserting a duplicate key is not possible. (All Rows in the target has the field PRPR_Origin set, so only those get minded in the command.
From Technet:
WHEN NOT MATCHED BY SOURCE THEN
Specifies that all rows of target_table that do not match the rows returned by ON , and that satisfy any additional search condition, are either updated or deleted according to the clause.
There does not stand anything from an insert command.
Does anybody has an advice for me? Where does my brain mislead me?
EDIT:
Source and target table are named alike but not the same. They are not even in the same database..
The source table is an extended version of the target with only one key: Staging_ID.
The target table has the same schema but not the Staging_ID column. In the target the PK is a combination of PRPR_CPC, PRPR_Vertriebsweg, RPR_Preistyp and PRPR_Gueltig_bis.
Some example data with PK marked yellow:
Source
Target
But what I really don't get is, why my statement creates an insert with the "WHEN NOT MATCHED BY SOURCE", that behaviour does not fit to the MS Technet article.
EDIT:
For anyone to stumble upon this thread, experiencing something similar.
The Problem here was, that the merge command executes as a bulk operation. In my case this lead temporary to primary key violations, while executing the query.

Synchronizing 2 tables with MERGE

I’ve been tasked to synchronize 2 tables (both are identical). They have 60 columns each. Table A is the primary table that will be initially filled. I need to create a stored procedure (done) that will merge these 2 tables and populate both with the same exact data (Update, insert, delete) when called. How would I use the MERGE function in SQL to achieve this? I’ve looked at both the MSDN documentation and similar that’s on technet, but I’m pretty confused on getting started. Do I need to specify each field I need merged? Or is it a simple call I’m missing that will perform this action?
Here is a link to a simple example of the MERGE statement:
http://www.simple-talk.com/sql/learn-sql-server/the-merge-statement-in-sql-server-2008/
The basic syntax reads as:
MERGE table1
USING table2
ON table1.id = table2.id
WHEN MATCHED THEN
--Do an update here
WHEN NOT MATCHED BY TARGET THEN
--Do an insert here (or a delete)
;
You can also use WHEN NOT MATCHED BY SOURCE
Over 60 columns is a great number! When I need to sync 2 identical table I do:
;WITH tbl_to_synch as (
-- Prepare table to update,
Select *,chk = CHECKSUM(*) from [dbo].[tableA]
)
MERGE tbl_to_synch as [Target]
USING (Select *,chk = CHECKSUM(*) from [dbo].[tableB]) as [source]
ON [Target].key = [source].key
WHEN MATCHED AND [Target].chk <> [source].chk THEN
-- UPDATE ONLY row that is changed
UPDATE
SET
column01 = [source].[column01]
,column02 = [source].[column01]
-- ....
,column59 = [source].[column59]
,column60 = [source].[column59]
WHEN NOT MATCHED BY TARGET THEN
insert (column01, column02, ...,column59,column60)
values (column01, column02, ...,column59,column60)
WHEN NOT MATCHED BY SOURCE THEN DELETE
-- Show what is changed
OUTPUT $action, ISNULL(INSERTED.key,DELETED.key);