Is Oracle MERGE NOT MATCHED THEN UPDATE possible? - sql

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.

Related

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!

Bigquery Error: UPDATE/MERGE must match at most one source row for each target row

Just wondering if someone could help with the following error:
UPDATE/MERGE must match at most one source row for each target row
My query is as below:
UPDATE `sandbox.sellout` s
SET s.SKU_Label = TRIM(SKU_TEMP.SKU)
FROM (SELECT SKU, Old_SKU FROM `sandbox.ref_sku_temp`) SKU_TEMP
WHERE TRIM(SKU_TEMP.Old_SKU) = TRIM(s.SKU)
If a row in the table to be updated joins with more than one row from the FROM clause, then the query generates the following runtime error: UPDATE/MERGE must match at most one source row for each target row.
Data Manipulation Language Syntax.
It occurs because the target table of the BigQuery contains duplicated row(w.r.t you are joining). If a row in the table to be updated joins with more than one row from the FROM clause, then BigQuery returns this error:
Solution
Remove the duplicated rows from the target table and perform the UPDATE/MERGE operation
Define Primary key in BigQuery target table to avoid data redundancy
Issue is with the duplicate rows in the source table. Please consider removing the the dups and run the query. Or post the sample data here.
Add DISTINCT to your select statement like this:
UPDATE `sandbox.sellout` s
SET s.SKU_Label = TRIM(SKU_TEMP.SKU)
FROM (SELECT DISTINCT SKU, Old_SKU FROM `sandbox.ref_sku_temp`) SKU_TEMP
WHERE TRIM(SKU_TEMP.Old_SKU) = TRIM(s.SKU)

Using multiple statements in WHEN MATCHED

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>;

Using MERGE in SQL Server 2012 to insert/update data

I am using SQL Server 2012 and have two tables with identical structure. I want to insert new records from table 1 to table 2 if they don't already exist in table 2.
If they already exist, I want to update all of the existing records in table 2.
There are some 30 columns in my tables and I want to update all of them.
Can someone please help with this? I had a look at various links posted over internet, but quite don't understand how my statement should look like.
It's really not that hard....
You need:
a source table (or query) to provide data
a target table to merge it into
a condition on which those two tables are checked
a statement what to do if a match (on that condition) is found
a statement what to do if NO match (on that condition) is found
So basically, it's something like:
-- this is your TARGET table - this is where the data goes into
MERGE dbo.SomeTable AS target
-- this is your SOURCE table where the data comes from
USING dbo.AnotherTable AS source
-- this is the CONDITION they have to "meet" on
ON (target.SomeColumn = source.AnotherColumn)
-- if there's a match, so if that row already exists in the target table,
-- then just UPDATE whatever columns in the existing row you want to update
WHEN MATCHED THEN
UPDATE SET Name = source.Name,
OtherCol = source.SomeCol
-- if there's NO match, that is the row in the SOURCE does *NOT* exist in the TARGET yet,
-- then typically INSERT the new row with whichever columns you're interested in
WHEN NOT MATCHED THEN
INSERT (Col1, Col2, ...., ColN)
VALUES (source.Val1, source.Val2, ...., source.ValN);

Postgres UPDATE..FROM query with multiple updates on the same row

I am attempting to optimise a bulk UPDATE statement in Postgres using the UPDATE..FROM syntax to update from a list of values. It works except when the same row might be updated more than once in the same query.
For example say I have a table "test" with columns "key" and "value".
update test as t set value = v.value from (values
('key1', 'update1'),
('key1', 'update2') )
as v (key, value)
where t.key = v.key;
My desired behavior is for the row with key 'key1' to be updated twice, finishing with value set to 'update2'. In practice sometimes the value is updated to update1 and sometimes to update2. Also an update trigger function on the table is only invoked once.
The documentation (http://www.postgresql.org/docs/9.1/static/sql-update.html) explains why:
When a FROM clause is present, what essentially happens is that the target table is joined to the tables mentioned in the from_list, and each output row of the join represents an update operation for the target table. When using FROM you should ensure that the join produces at most one output row for each row to be modified. In other words, a target row shouldn't join to more than one row from the other table(s). If it does, then only one of the join rows will be used to update the target row, but which one will be used is not readily predictable.
Because of this indeterminacy, referencing other tables only within sub-selects is safer, though often harder to read and slower than using a join.
Is there any way to reformulate this query to achieve the behavior I'm looking for? Does the reference to sub-selects in the documentation give a hint?
Example (assuming id is a PK in the target table, and {id, date_modified} is a PK in the source table)
UPDATE target dst
Set a = src.a , b = src.b
FROM source src
WHERE src.id = dst.id
AND NOT EXISTS (
SELECT *
FROM source nx
WHERE nx.id = src.id
-- use an extra key field AS tie-breaker
AND nx.date_modified > src.date_modified
);
(in fact, this is deduplication of the source table -> forcing the source table to the same PK as the target table)