SQL merge command on one table - sql

I am trying to get my head over MERGE sql statement. What I want to achieve is:
Insert new values into the CSScolorOrders table but update corQuantity column if the record with colID and ordID already exist
This is what I ended up with:
MERGE INTO CSScolorOrders AS TARGET
USING (SELECT * FROM CSScolorOrders WHERE ordID = 3) AS SOURCE
ON (SOURCE.colID = 1) WHEN
MATCHED THEN UPDATE SET corQuantity = 1
WHEN
NOT MATCHED BY TARGET
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
Unfortunately it does not raise any exception so I do not know why it doesn't work.

As discussed here, you'll see that a merge is exactly as it sounds. taking two tables and searching for the value you joined them on lets call it "X". if X is a match then you perform an update on that record. If it does not exist then you would perform an insert on the targeted table using the values selected.
In your case i'm not entirely sure if your join
( ON (SOURCE.colID = 1) )
is correct. i'm pretty sure this needs to be
on(Source.colID = Target.colID)
So the full statement should be this:
MERGE INTO CSScolorOrders AS TARGET
USING (SELECT * FROM CSScolorOrders WHERE ordID = 3) AS SOURCE
on(Source.colID = Target.colID)
WHEN MATCHED THEN
UPDATE SET corQuantity = 1
WHEN NOT MATCHED BY TARGET
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
but i haven't tested this and am not 100% sure what your table columns are and what exactly you're attempting to join. But the link i provided should point you in the right direction.
Hope this helps!

MERGE INTO accounting_values AS Target
USING (select #entity_id as entity_id, #fiscal_year as fiscal_year, #fiscal_period as fiscal_period) AS source
ON (target.entity_id = source.entity_id AND target.fiscal_year = source.fiscal_year AND target.fiscal_period = source.fiscal_period)
WHEN MATCHED THEN
UPDATE SET value = #value
WHEN NOT MATCHED BY Target THEN
INSERT
(entity_id, fiscal_year, fiscal_period, value)
VALUES (#entity_ID, #fiscal_year, #fiscal_period, #value);
This is tested. It doesn't reference the table twice. I was trying to duplicate MySQL's ON DUPLICATE KEY UPDATE.

MERGE CSScolorOrders AS TARGET
USING (SELECT * FROM CSScolorOrders WHERE ordID = 3) AS SOURCE
ON (SOURCE.colID = TARGET.colID) WHEN
MATCHED THEN UPDATE SET corQuantity = 1
WHEN
NOT MATCHED
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);

To UPDATE or INSERT in a table whether this table contains a record or not, you can you MERGE as follows:
ensure USING clause returns ONE entry
express the matching condition(s) in the ON clause
In your case (example below tested on Oracle):
MERGE INTO CSScolorOrders AS TARGET
USING (SELECT 'OneEntry' FROM DUAL) AS SOURCE
ON (colID = 1 and ordID = 3) WHEN
MATCHED THEN UPDATE SET corQuantity = 1
WHEN
NOT MATCHED BY TARGET
THEN INSERT (colID, ordID, corQuantity) VALUES (1, 3, 1);
Most databases have a DUMMY table allowing arbitrary SELECT (above, I use DUAL), but if we only use the table mentioned in your question, you can e.g. replace
SELECT 'OneEntry' FROM DUAL
with
SELECT COUNT(*) FROM CSScolorOrders

Related

Snowflake: unable to reference column in target table inside a case predicate for notMatchedClause in a MERGE command

It seems like I am able to reference a column the target table in a case predicate for a matchedClause in a MERGE command, but am unable to do so in a notMatchedClause.
For example I create two tables and insert some values to them as below.
create table test_tab_a (
name string,
something string
);
create table test_tab_b (
name string,
something string
);
insert into test_tab_a values ('a', 'b');
insert into test_tab_a values ('c', 'z');
insert into test_tab_b values ('a', 'c');
insert into test_tab_b values ('c', 'z');
Then run a merge command as below and works just fine.
merge into public.test_tab_a as target
using (
select * from public.test_tab_b
) src
on target.name = src.name
when matched and target.SOMETHING = src.something then delete;
However when I run a command using a not matched clause, I get an invalid identifier error.
merge into public.test_tab_a as target
using (
select * from public.test_tab_b
) src
on target.name = src.name
when not matched and b.SOMETHING != a.something then insert values (name, something);
Why is the case_predicate evaluated differently depending on the type of clause?
Interesting find, I get the same thing, do not see anything in the documentation mentioning it is not available. So, I would recommend submitting a support case with Snowflake.
As a workaround, you could add logic to your subselect by joining to test_tab_a, like this:
merge into test_tab_a as a
using (
select test_tab_b.name, test_tab_b.something
from test_tab_b sub_b
inner join test_tab_a sub_a on sub_b.name = sub_a.name
where sub_b.SOMETHING != sub_a.something
) b
on a.name = b.name
when not matched then insert values (name, something);
Adding some more detail. I also checked other rdbms and haven't seen specific documentation if they support this behavior either (specifically WHEN NOT MATCHED + AND condition referencing target table). Is this query pattern coming from another rdbms?
Are there other pieces of this Merge that have been left off for simplicity? It seems like an Insert/Left Join is more useful than a Merge in this case.
insert into public.test_tab_a
select b.name,b.something from public.test_tab_b b
left join public.test_tab_a a
on a.name = b.name
where a.something != b.something
I will raise a Snowflake feature request for this use-case.

Oracle Merge statement not inserting data

I have came up with below sql statement on oracle 11g for merge the data.
MERGE INTO myTable tgt
USING ( SELECT myTable.ROWID AS rid
FROM myTable
WHERE myTable.myRef = 'uuuu' or
myTable.name = 'sam'
--union
--select rowId,null from dual
) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.name='name_worked'
when not matched then
insert (
tgt.myRef,tgt.name) values ('RRRR','HHH');
I need to insert data except ID column here which will manage from the trigger for primary key insertion. due to error ORA-38104: Columns referenced in the ON Clause cannot be updated i used RowId approach here.
now my issue is merge statement works fine when it comes to update but fail in inserting data in this situation.
I went through this post and added the union statement. but still it fails when goes to insert duplicate record due to the constraint in my table instead of running it smoothly.
Can anyone please help me out here? Appreciate it a lot. thank you in advanced.
==========Edited===========
Please find the complete code sample and the error messages below.
MERGE INTO myTable tgt
USING ( SELECT myTable.ROWID AS rid
FROM myTable
WHERE myTable.myRef = 'RRRR' or
myTable.mytablename = 'sam'
union
select rowId from dual
) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.mytablename='myt_name', tgt.name='nameA'
when not matched then
insert (
tgt.mytableid,tgt.mytablename,tgt.name,tgt.myRef) values (11,'RRRR','HHH','myref1');
and my table is -
CREATE TABLE "sa"."MYTABLE"
(
"MYTABLEID" NUMBER NOT NULL ENABLE,
"MYTABLENAME" VARCHAR2(20 BYTE) NOT NULL ENABLE,
"NAME" VARCHAR2(20 BYTE),
"MYREF" VARCHAR2(20 BYTE),
CONSTRAINT "MYTABLE_PK" PRIMARY KEY ("MYTABLEID", "MYTABLENAME")
)
if i run this first time it will insert the record as expected.
when i run it the second time my expectation is it should match the myRef = 'RRRR' and do the update. i intentionally put 'or' between condition because if i find any value exist in the table it should go and update the existing record.
but instead of doing that update it will throw this error because merge statement try to insert again.
SQL Error: ORA-00001: unique constraint (sa.MYTABLE_PK) violated
00001. 00000 - "unique constraint (%s.%s) violated"
my requirement is when it run the first time it should insert and when i run the same again it should pick that record and update it. Please let me know what to adjust in my merge statement in order to work as expected here. Thanks in advance.
The below MERGE query is what you are looking for.
MERGE INTO myTable tgt
USING ( select x.rid from
(SELECT myTable.ROWID AS rid
FROM myTable
WHERE myTable.myRef IN ('myref1','uuuu')) x
right outer join dual
on x.rowid <> dual.rowid
) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.mytablename='myt_name', tgt.name='nameA'
when not matched then
insert (tgt.mytableid,tgt.mytablename,tgt.name,tgt.myRef) values (mytable_seq.nextval,'RRRR','HHH','myref1');
There have been 3 changes made from the query that you provided.
Inside the 'using' subquery 'RRRR' was being checked in myTable.myRef column, but while inserting 'myref1' was being inserted in the same. Hence this has been changed in the using subquery to check 'myref1'.
'uuuu' has been introduced in the same check.
DUAL tas been introduced in right outer join.
The query will process as below -
1.During first run, there would be no row in mytable. Hence the right outer join with dual will prevail. This will enable the insert to happen.
During 2nd run, there will be a row with myRef column of 'myref1'. Its Rowid will be picked up and the Update will happen. After update, myRef column will be updated to 'uuuu'.
3.During all subsequent runs, 1 row will always be returned in the inside using subquery, this time because of the column value of 'uuuu'. This will enable the update to happen , which will again update the columns with the same existing values.
Thus in effect, 1st time INSERT will happen and in all subsequent times UPDATE will take place.
It appears you want something like the following:
MERGE INTO MYTABLE t
USING (SELECT newID, newTable_name from DUAL) d
ON (t.MYTABLEID = d.newID AND
t.MYTABLENAME = d.newTable_name)
WHEN MATCHED THEN
UPDATE SET NAME = newName,
MYREF = newRef
WHEN NOT MATCHED THEN
INSERT (MYTABLEID, MYTABLENAME, NAME, MYREF)
VALUES (newID, newTable_name, newName, newRef)
where the variables newID, newTable_name, newName, and newRef are populated from whatever source of data you're using.
Also - do you really want the primary key to be (MYTABLEID, MYTABLENAME)? Can you actually have multiple rows with the same MYTABLEID value? And do you really want to allow multiple rows having the same MYTABLENAME value? My thought is that the primary key should be MYTABLEID, with a UNIQUE constraint on MYTABLENAME. If this is the case then MYTABLEID is redundant, and MYTABLENAME could be used as the primary key. ????
Best of luck.
I was able to make this work on below query.
MERGE INTO myTable tgt
USING ( SELECT (SELECT myTable.ROWID AS rid
FROM myTable
WHERE myTable.myRef = '2' or
myTable.mytablename = 'sam'
) as rid from dual) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.mytablename='test1', tgt.name='nameA'
when not matched then
insert (
tgt.mytableid,tgt.mytablename,tgt.name,tgt.myRef) values (11,'RRRR1','1','2');
first time it will insert the row and for subsequent runs it will update the row.

Insert first and then update the table using SQL statement

I am trying to use insert and update sql statements.
My table is as follows:
|c1|c2|c3|c4|c5
|1 2 a b c
|1 3 e f g
c3,c4,c5 can have different values. The row can be unique with the combination of C1 and C2 column. I need to be able to check if first row doesn't exists with values c1,c2 then insert the data. If c1,c2 already have the values for eg (1,2) and if the data comes back with the same values for c1,c2 then update c3,c4,c5 with the latest values.
I tried using the following query
INSERT INTO t1 (c1,c2,c3,c4,c5)
VALUES ('1','2','a','b','c')
ON DUPLICATE KEY
UPDATE c3='e',c4 = 'f',c5='g';
I am getting a ORA error as follows
SQL Command not ended properly (ORA-00933)
Update after response from sagi
MERGE INTO table1 t
USING(select '000004' as SENDER,'Receiver' as RECEIVER ,'1030' as IDENTIFIER,'2016' as CREATIONDATEANDTIME,'2' as ACKCODE,'Test' as ACKDESCRIPTION from table1 ) s
ON(t.SENDER = s.SENDER and t.IDENTIFIER = s.IDENTIFIER)
WHEN MATCHED THEN UPDATE SET t.CREATIONDATEANDTIME = '1213',t.RECEIVER = 'hello'
WHEN NOT MATCHED THEN INSERT (t.SENDER,t.RECEIVER,t.IDENTIFIER,t.CREATIONDATEANDTIME,t.ACKCODE,t.ACKDESCRIPTION)
VALUES (s.SENDER,s.RECEIVER,s.IDENTIFIER,s.CREATIONDATEANDTIME,s.ACKCODE,s.ACKDESCRIPTION)
Output of query:
scenario 1: When there is no data matching the condition(t.SENDER = s.SENDER and t.IDENTIFIER = s.IDENTIFIER), I get an error as follows
ORA-30926: Unable to get stable set of rows in the source tables. Cause: A stable set of rows could not be got because of large dml activity or a non-deterministic activity where clause.
Action: Remove any non-deterministic where clause and reissue dml
Scenario 2: When there is data matching the condition (t.SENDER = s.SENDER and t.IDENTIFIER = s.IDENTIFIER) then in the table, I can see 5 new entries.
Can you please help.
You can use MERGE STATEMENT like this:
MERGE INTO t1 t
USING(select '1' as c1,'2' c2 ,'a' as c3,'b' as c4,'c' as c5 from dual) s
ON(t.c1 = s.c1 and t.c2 = s.c2)
WHEN MATCHED THEN UPDATE SET t.c3 = '1213',t.c4 = 'test'
WHEN NOT MATCHED THEN INSERT (t.c1,t.c2,t.c3,t.c4,t.c5)
VALUES (S.c1,s.c2,s.c3,s.c4,s.c5)
This basically perform an UPSERT, update else insert. It checks if the values exist, if so - update/deletes them(adjust to code to do what you want) and if not, insert them.
MERGE INTO table1 t USING(select distinct '000004' as SENDER,'Receiver' as RECEIVER ,'1030' as IDENTIFIER,'2016' as CREATIONDATEANDTIME,'2' as ACKCODE,'Test' as ACKDESCRIPTION from table1 ) s ON(t.SENDER = s.SENDER and t.IDENTIFIER = s.IDENTIFIER) WHEN MATCHED THEN UPDATE SET t.CREATIONDATEANDTIME = '1213',t.RECEIVER = 'hello' WHEN NOT MATCHED THEN INSERT (t.SENDER,t.RECEIVER,t.IDENTIFIER,t.CREATIONDATEANDTIME,t.ACKCODE,t.ACKDESCRIPTION) VALUES (s.SENDER,s.RECEIVER,s.IDENTIFIER,s.CREATIONDATEANDTIME,s.ACKCODE,s.ACKDESCRIPTION)
Added distinct clause in my query and it is working fine. Thanks all for replying to my post and guiding me.

Oracle Merge/Insert

I'm trying to see if there is an existing 'IMPORTID' in a table consisting of two columns: 'IMPORTID','STEP'. If there is, increment the record's STEP column by 1.
I've just tested using hardcoded values and there are no rows in the table currently. I'm expecting the following SQL to insert an entry but nothing is happening.
MERGE into TOSTEP t
USING
(SELECT 'Test' IMPORTID, 1 STEP FROM TOSTEP) s
ON
(s.IMPORTID = t.IMPORTID)
WHEN MATCHED THEN
update set t.STEP = s.STEP +1
WHEN NOT MATCHED THEN
insert (IMPORTID,STEP)
values ('Test',1);
You are merging an empty table with itself..meaning there is no data selected in using clause. Change (SELECT 'Test' IMPORTID, 1 STEP FROM TOSTEP) s into (SELECT 'Test' IMPORTID, 1 STEP FROM DUAL) s and it should work
FULL CODE:
MERGE into TOSTEP t
USING
(SELECT 'Test' IMPORTID, 1 STEP FROM DUAL) s
ON
(s.IMPORTID = t.IMPORTID)
WHEN MATCHED THEN
update set t.STEP = s.STEP +1
WHEN NOT MATCHED THEN
insert (IMPORTID,STEP)
values (s.IMPORTID, s.STEP);

SQL With... Update

Is there any way to do some kind of "WITH...UPDATE" action on SQL?
For example:
WITH changes AS
(...)
UPDATE table
SET id = changes.target
FROM table INNER JOIN changes ON table.id = changes.base
WHERE table.id = changes.base;
Some context information: What I'm trying to do is to generate a base/target list from a table and then use it to change values in another table (changing values equal to base into target)
Thanks!
You can use merge, with the equivalent of your with clause as the using clause, but because you're updating the field you're joining on you need to do a bit more work; this:
merge into t42
using (
select 1 as base, 10 as target
from dual
) changes
on (t42.id = changes.base)
when matched then
update set t42.id = changes.target;
.. gives error:
ORA-38104: Columns referenced in the ON Clause cannot be updated: "T42"."ID"
Of course, it depends a bit what you're doing in the CTE, but as long as you can join to your table withint that to get the rowid you can use that for the on clause instead:
merge into t42
using (
select t42.id as base, t42.id * 10 as target, t42.rowid as r_id
from t42
where id in (1, 2)
) changes
on (t42.rowid = changes.r_id)
when matched then
update set t42.id = changes.target;
If I create my t42 table with an id column and have rows with values 1, 2 and 3, this will update the first two to 10 and 20, and leave the third one alone.
SQL Fiddle demo.
It doesn't have to be rowid, it can be a real column if it uniquely identifies the row; normally that would be an id, which would normally never change (as a primary key), you just can't use it and update it at the same time.