SQL MERGE: Is it possible to ignore a MATCH? - sql

Here's my merge statement:
MERGE PE_TranslationPhrase T
USING PE_TranslationPhrase_Staging S
ON (T.CultureName = S.CultureName AND T.Phrase = S.Phrase)
WHEN MATCHED
THEN UPDATE SET T.TranslationId = T.TranslationId -- do nothing
WHEN NOT MATCHED BY TARGET
THEN INSERT (TranslationId, CultureName, Phrase)
VALUES (S.TranslationId, S.CultureName, S.Phrase);
I seem to be having trouble with the WHEN MATCHED section. Based on this question and answer, I changed the WHEN MATCHED to:
THEN UPDATE SET T.TranslationId = T.TranslationId
But I am still getting this error:
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.
I get the reason why. Based on the ON statement, I am getting duplicate matches. But... I don't care. If a record from the source table already exists in the target table, then I want to just do nothing. Don't update anything at all. Skip it.
Is that possible?

Oh. Turns out you don't need the 'WHEN MATCHED' statement. I thought it was required.
MERGE PE_TranslationPhrase T
USING PE_TranslationPhrase_Staging S
ON (T.CultureName = S.CultureName AND T.Phrase = S.Phrase)
WHEN NOT MATCHED BY TARGET
THEN INSERT (TranslationId, CultureName, Phrase)
VALUES (S.TranslationId, S.CultureName, S.Phrase);

If no update is being done, have you considered just doing the insert using rows from PE_TranslationPhrase_Staging that aren't in PE_TranslationPhrase?
CTE Approach:
WITH CTE AS (
SELECT
S.TranslationId,
S.CultureName,
S.Phrase
FROM PE_TranslationPhrase_Staging S
LEFT JOIN PE_TranslationPhrase T on S.Phrase = T.Phrase and S.CultureName = T.CultureName
WHERE T.PHRASE IS NULL
)
INSERT INTO PE_TranslationPhrase (TranslationId, CultureName, Phrase)
SELECT
TranslationId,
CultureName,
Phrase
FROM CTE
Subquery Approach:
INSERT INTO PE_TranslationPhrase (TranslationId, CultureName, Phrase)
SELECT
TW.TranslationId,
TW.CultureName,
TW.Phrase
FROM (
SELECT
S.TranslationId,
S.CultureName,
S.Phrase
FROM PE_TranslationPhrase_Staging S
LEFT JOIN PE_TranslationPhrase T on S.Phrase = T.Phrase and S.CultureName = T.CultureName
WHERE T.PHRASE IS NULL ) TW

Unfortunately, what can not be done in the MERGE statement to omit this error. But I have solved this by generating an identity column (NOT persistent) that allows me to add a unique value to each record for both the source and target of the sentence and compare the form. Something like this:
ROW_NUMBER () OVER (PARTITION BY CultureName, Phrase ORDER BY CultureName, Phrase)

No, you cannot use Merge ... When Matched ... in such cases that there are multiple matches and as you could find out, you can simply remove the When Matched section. However if you needed this section for update, instead of using Merge you can use the following code:
update PE_TranslationPhrase
set TranslationId = S.xxx
from
PE_TranslationPhrase T, PE_TranslationPhrase_Staging S
where T.CultureName = S.CultureName AND T.Phrase = S.Phrase

Related

PostgreSQL can't UPSERT with a "WITH"

I want to upsert a value with a WITH, like this:
WITH counted as (
SELECT votant, count(*) as nbvotes
FROM votes
WHERE votant = '123456'
GROUP BY votant
)
INSERT INTO badges(id, badge, conditions, niveau, date_obtention)
VALUES('123456', 'category', c.nbvotes, 1, current_timestamp)
ON CONFLICT (id, badge)
DO UPDATE badges b
SET b.conditions = c.nbvotes
FROM counted c
WHERE b.id = c.votant AND b.badge = 'category'
The console tells me I have an error on "badges" just after "DO UPDATE"
I really don't understand what goes wrong here, if anybpdy could give me a hand, it would be great :)
As documented in the manual the badges b after the do update part is wrong - and unnecessary if you think of it. The target table is already defined by the INSERT part.
But you also don't need a FROM or join to the original value.
So just use:
...
ON CONFLICT (id, badge)
DO UPDATE
SET conditions = '{"a":"loooool"}';
If you need to access the original values, you can use the excluded record to refer to it, e.g.
SET conditions = EXCLUDED.conditions
which in your case would refer to the rows provided in the values clause ({"a":"lol"}' in your example)
And target columns of an UPDATE cannot be table-qualified. So just SET conditions = ...
If you want to use the result of the CTE as the source of the INSERT, you need to use an INSERT ... SELECT. You can't use a FROM clause in the DO UPDATE part of an INSERT.
WITH counted as (
SELECT votant, count(*) as nbvotes
FROM votes
WHERE votant = '123456'
GROUP BY votant
)
INSERT INTO badges(id, badge, conditions, niveau, date_obtention)
SELECT '123456', 'category', c.nbvotes, 1, current_timestamp
FROM counted c
ON CONFLICT (id, badge)
DO UPDATE
SET conditions = excluded.conditions

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.

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

How do I use the GROUP BY clause in a SQL MERGE statement?

I am trying to merge two tables and I would like to use GROUP BY in order to fix the following error:
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.
Where exactly would the GROUP BY clause go?
MERGE dbo.MyTarget targ
USING dbo.MySource src
ON (targ.Identifier = src.Identifier
AND targ.Name = src.ConstituentName
AND targ.Ticker = src.ConstituentTicker
AND (targ.CUSIP = src.CUSIP OR targ.ISIN = src.ISIN OR targ.SEDOL = src.SEDOL))
WHEN MATCHED THEN
-- update values
;
Something like this:
MERGE dbo.MyTarget targ
USING (SELECT ... FROM dbo.MySource GROUP BY .....) src
ON (targ.Identifier = src.Identifier
AND targ.Name = src.ConstituentName
AND targ.Ticker = src.ConstituentTicker
AND (targ.CUSIP = src.CUSIP OR targ.ISIN = src.ISIN OR targ.SEDOL = src.SEDOL))
WHEN MATCHED THEN
-- update values
;

Prevent the insertion of duplicate rows using SQL Server 2008

I am trying to insert some data from one table into another but I would like to prevent the insertion of duplicate rows. I have currently the following query:
INSERT INTO Table1
(
Table1Col1,
Table1Col2,
Table1Col3,
Table1Col4,
Table1Col5
)
SELECT
Table2Col1,
Table2Col2 = constant1,
Table2Col3 = constant2,
Table2Col4 = constant3,
Table2Col5 = constant4
FROM Table2
WHERE
Condition1 = constant5
AND
Condition2 = constant6
AND
Condition3 = constant7
AND
Condition4 LIKE '%constant8%'
What I do not know is that the row I am trying to insert from Table2 into Table1 might already exist and I would like to prevent this possible duplication from happening and skip the insertion and just move onto inserting the next unique row.
I have seen that I can use a WHERE NOT EXISTS clause and use of the INTERSECT keyword but I did not fully understand how to apply it to my particular query as I only want to use some of the selected data from Table2 and then some constant values to insert into Table1.
EDIT:
I should add that the columns TableCol2 through to TableCol5 don't actually exist in the result set and I am just populating these columns alongside Table2Col1 that is returned.
Since you are on SQL Server 2008, you can use a merge statement.
You can easily check if a row exists base on a key
something like this:
merge TableMain AS target
using TableA as source
ON <join tables here>
WHEN MATCHED THEN <update>
WHEN NOT MATCHED BY TARGET <Insert>
WHEN NOT MATCHED BY SOURCE <delete>
Intersect (minus in Sql Server's terms) is out of question because it compares whole row. Other two options are not in/not exists/left join and merge. Not In is for single-column prinary key only, so it is out of question in this instance. In/Exists/Left join should have the same performance in Sql Server, so I'll just use exists:
INSERT INTO Table1
(
Table1Col1,
Table1Col2,
Table1Col3,
Table1Col4,
Table1Col5
)
SELECT
Table2Col1,
Table2Col2 = constant1,
Table2Col3 = constant2,
Table2Col4 = constant3,
Table2Col5 = constant4
FROM Table2
WHERE
Condition1 = constant5
AND
Condition2 = constant6
AND
Condition3 = constant7
AND
Condition4 LIKE '%constant8%'
AND NOT EXISTS
(
SELECT *
FROM Table1 target
WHERE target.Table1Col1 = Table2.Table2Col1
AND target.Table1Col2 = Table2.Table2Col2
AND target.Table1Col3 = Table2.Table2Col3
)
Merge is used to sync two tables; it has ability to insert, update and delete records from target table.
merge into table1 as target
using table2 as source
on target.Table1Col1 = source.Table2Col1
AND target.Table1Col2 = source.Table2Col2
AND target.Table1Col3 = source.Table2Col3
when not matched by target then
insert (Table1Col1,
Table1Col2,
Table1Col3,
Table1Col4,
Table1Col5)
values (Table2Col1,
Table2Col2,
Table2Col3,
Table2Col4,
Table2Col5);
If columns from table2 are computed during transfer, in not exists() case you might use derived table in place of table2, and the same applies to merge example - just place your query in place of reference to table2.
we have check the whether the data is already exist or not in table. For this we have to use If condition to avoid the duplicate insertion