Insert first and then update the table using SQL statement - sql

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.

Related

MERGE statement to update or insert rows into a table

My task is to insert or update rows in a table2. Table1 contains id's of all employees. That id matches the ID in the table2. Some of the employees in table2 already have the rows I need but some don't. Table2 doesn't contain the ID's of the employees that don't have those rows.
My task is to update the rows for the existing ID's and insert for the ones that don't have those rows.
I have tried the following statement:
MERGE INTO dbo.table2 AS TGT
USING (SELECT table1ID FROM dbo.table1) AS SRC
ON SRC.table1ID = TGT.table2ID
WHEN MATCHED
AND table2Code = 'ValueToInsertOrUpdateCode'
THEN
UPDATE
SET table2Value= 'ValueToInsertOrUpdateValue'
WHEN NOT MATCHED BY TARGET
THEN
INSERT (table2Code, table2ID, table2Value)
VALUES ('ValueToInsertOrUpdateCode', src.table1ID, 'ValueToInsertOrUpdateValue');
This currently only updates the rows that exist, but doesn't insert the rows for ID's that don't have existing rows.
Based on your comments is sounds like you want this so that the WHEN NOT MATCHED BY TARGET is executed:
MERGE INTO dbo.table2 AS TGT
USING (SELECT table1ID FROM dbo.table1) AS SRC
ON (SRC.table1ID = TGT.table2ID AND table2Code = 'ValueToInsertOrUpdateCode') -- This is the difference
WHEN MATCHED
AND table2Code = 'ValueToInsertOrUpdateCode'
THEN
UPDATE
SET table2Value= 'ValueToInsertOrUpdateValue'
WHEN NOT MATCHED BY TARGET
THEN
INSERT (table2Code, table2ID, table2Value)
VALUES ('ValueToInsertOrUpdateCode', src.table1ID, 'ValueToInsertOrUpdateValue');
WHEN NOT MATCHED BY TARGET would not execute when SRC.table1ID = TGT.table2ID (i.e. they match).
Updating the ON clause to ON (SRC.table1ID = TGT.table2ID AND table2Code = 'ValueToInsertOrUpdateCode') will give you the inserts you are expecting.
However you should probably not do this:
ON <merge_search_condition> Caution
It's important to specify only the columns from the target table to use for matching purposes. That is, specify columns from the target table that are compared to the corresponding column of the source table. Don't attempt to improve query performance by filtering out rows in the target table in the ON clause; for example, such as specifying AND NOT target_table.column_x = value. Doing so may return unexpected and incorrect results.
For this reason and what others have suggested it would be safer to do separate update and insert statements.
I would, honestly, suggest avoiding the MERGE operator and doing an Upsert here instead. For your scenario, what you need is most likely the following:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
UPDATE T2 WITH (UPDLOCK, SERIALIZABLE)
SET table2Value = 'ValueToInsertOrUpdateValue'
FROM dbo.Table2 T2
JOIN dbo.Table1 T1 ON T1.table1ID = T2.table2ID;
-- You could honestly use an EXISTS here, considering that you're updating the table
-- with a literal, rather than a value from the table Table1.
INSERT INTO dbo.Table2 (table2Code , table2ID, table2Value)
SELECT 'ValueToInsertOrUpdateCode',
T1.table1ID,
'ValueToInsertOrUpdateValue'
FROM dbo.Table1 T1
WHERE NOT EXISTS (SELECT 1
FROM dbo.Table2 T2
WHERE T2.table2ID = T1.table1ID);
COMMIT;
db<>fiddle

BigQuery MERGE Query: Merge tables based on condition

I was playing with the MERGE query in BigQuery and noticed that I can't update the specific rows based on the condition.
For example, I have 5 records already present in the table. I want to update only two records whose values are changed. When I execute the below query all the rows get updated. That means out of 5 for 3 records I don't want to change the values. AS soon as I'll receive new values that should change the existing records.
MERGE `test.organization_user` T
USING `test.user_details` S
ON T.user_id = S.user_id
WHEN MATCHED AND
(
T.organization <> S.organization OR
T.contact_number <> S.contact_number
)
THEN
UPDATE
SET
T.organization = S.organization,
T.contact_number = S.contact_number
WHEN NOT MATCHED THEN
INSERT ROW
Is there any solution for this kind of scenario or using merge it will update all the matching records and if we don't want to update the existing one then should we have values for all the fields for those records in the source table(from which values will get updated to the target table)?
An example is:
You should handle null in condition:-
MERGE `test.organization_user` T
USING `test.user_details` S
ON T.user_id = S.user_id
WHEN MATCHED AND
(
IFNULL(T.organization,'') <> IFNULL(S.organization,'') OR
IFNULL(T.contact_number,0) <> IFNULL(S.contact_number,0)
)
THEN
UPDATE
SET
T.organization = S.organization,
T.contact_number = S.contact_number
WHEN NOT MATCHED THEN
INSERT ROW

How to get data from joined tables when performing a merge?

I am trying to use a merge to a table.
What I am having trouble with is getting the matching name from the code that exists in the original table. I will put my code and explain further:
MERGE INTO ResultTable R
USING InitialTable IT
ON (false)
WHEN MATCHED THEN -- do some stuff
WHEN NOT MATCHED THEN
INSERT (PrimaryKey,..., ThingFromJoinedTable)
VALUES (Seq.NEXTVAL, ..., ??? );
So the Initial table has a foreign key and I want to get the matching value in the Joined table.
Anyone have any idea on how to do so, I have tried having a nested select with a join, but it gives me a single-row subquery returns more than one row error.
Something like this:
MERGE INTO ResultTable R
USING ( SELECT it.this, it.that, third.this, third.that
FROM InitialTable it
JOIN ThirdTable third ON <your join criteria> ) SRC
/* depending on which columns you want for the join */
ON (r.col1 = src.col1 and r.col2 = src.col2)
WHEN MATCHED THEN -- do some stuff
/* depending on which columns you need to merge */
UPDATE SET
r.col4 = src.col4,
r.col5 = src.col5,
etc.
WHEN NOT MATCHED THEN
INSERT (PrimaryKey,..., colThis, colThat, ....)
VALUES (Seq.NEXTVAL, ..., src.colThis, src.colThat );

Oracle SQL, trying to get one value from a select/join to use to update one column in one table?

I have one table with the following columns:
T_RESOLVED_DATE
I_HOUSEHOLD_NUMBER
I_RESOLVED_SET_NUMBER
I_STATION_CODE
I_RESOLVED_START_MIN
I_DURATION
I_PERSON_NUMBER
I_COVIEW_DEMO_ID
Initially, I_COVIEW_DEMO_ID is set to null.
Then I have another table with the following columns:
T_RESOLVED_DATE
I_HOUSEHOLD_NUMBER
I_PERSON_NUMBER
I_AGE
T_GENDER
I_COVIEW_DEMO_ID
I am trying to update I_COVIEW_DEMO_ID in the first table by using the value of I_COVIEW_DEMO_ID in the second table where the T_RESOLVED_DATE, I_HOUSEHOLD_NUMBER, and I_PERSON_NUMBER are equal in both tables. The first table may contain multiple rows with the same DATE, HOUSEHOLD_NUMBER, and PERSON_NUMBER, because the rows can vary by the rest of the columns.
I have tried to do a select and a group by which seems to get me part way there, but I am getting a "single-row subquery returns more than one row" error when I try to update the columns in the first table. This is what I've tried, along with variations of it:
UPDATE
Table1
SET
I_COVIEW_DEMO_ID =
(SELECT
b.I_COVIEW_DEMO_ID
FROM Table1 a,
Table2 b
WHERE a.I_HOUSEHOLD_NUMBER = b.I_HOUSEHOLD_NUMBER AND
a.I_PERSON_NUMBER = b.I_PERSON_NUMBER AND
a.T_RESOLVED_DATE = b.T_RESOLVED_DATE
GROUP BY b.I_COVIEW_DEMO_ID);
Any suggestions?
I was able to get it to work using this statement:
MERGE INTO table1 a
USING
(
SELECT DISTINCT
T_RESOLVED_DATE,
I_HOUSEHOLD_NUMBER,
I_PERSON_NUMBER,
I_COVIEW_DEMO_ID
FROM
table2
) b
ON
(
a.T_RESOLVED_DATE = b.T_RESOLVED_DATE
AND a.I_HOUSEHOLD_NUMBER = b.I_HOUSEHOLD_NUMBER
AND a.I_PERSON_NUMBER = b.I_PERSON_NUMBER
) WHEN MATCHED THEN
UPDATE SET
a.I_COVIEW_DEMO_ID = b.I_COVIEW_DEMO_ID;
As per our discussion on the comments this would be a simple PLSQL block to do what you need. I'm doing direct from my head without test, so you may need to fix some sintaxe mistake.
BEGIN
FOR rs IN ( SELECT I_HOUSEHOLD_NUMBER,
I_PERSON_NUMBER,
I_COVIEW_DEMO_ID,
T_RESOLVED_DATE
FROM Table2 ) LOOP
UPDATE Table1
SET I_COVIEW_DEMO_ID = rs.I_COVIEW_DEMO_ID
WHERE I_PERSON_NUMBER = rs.I_PERSON_NUMBER
AND I_HOUSEHOLD_NUMBER = rs.I_HOUSEHOLD_NUMBER
AND T_RESOLVED_DATE = rs.T_RESOLVED_DATE;
END LOOP;
--commit after all updates, if there is many rows you should consider in
--making commits by blocks. Define a count and increment it whithin the for
--after some number of updates you commit and restart the counter
COMMIT;
END;

SQL merge command on one table

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