How to use Merge Command in Oracle - sql

I have query operation like below. How can I write single merge query where DWB_ACCT_CLSS_STG2 is stage table and DWB_ACCT_CLASS is the target table?
INSERT INTO DWB_ACCT_CLSS_STG2
SELECT
STG1.ACCT_CLSS_CD
,STG1.ACCT_CLSS_NME
,STG1.ACCT_CLSS_DSC
,STG1.PROCESS_NAME
,STG1.EXECUTION_ID
,STG1.FILE_ID
,STG1.FILE_DATE
,STG1.DATA_DATE
,STG1.LOAD_USER
,STG1.LOAD_DATE
,STG1.DW_LAST_UPDATE_TIME
FROM DWB_ACCT_CLSS_STG1 STG1
LEFT OUTER JOIN DWB_ACCT_CLASS TRGT
ON STG1.ACCT_TYP_CD = TRGT.ACCT_TYP_CD
WHERE TRGT.ACCT_TYP_CD IS NULL;
INSERT INTO DWB_ACCT_TYP_STG2
SELECT
STG1.ACCT_TYP_CD
,STG1.ACCT_TYP_DSC
,STG1.PROCESS_NAME
,STG1.EXECUTION_ID
,STG1.FILE_ID
,STG1.FILE_DATE
,STG1.DATA_DATE
,STG1.LOAD_USER
,STG1.LOAD_DATE
,STG1.DW_LAST_UPDATE_TIME
FROM DWB_ACCT_CLASS_STG1 STG1
INNER JOIN DWB_ACCT_CLASS TRGT
ON STG1.ACCT_TYP_CD = TRGT.ACCT_TYP_CD
WHERE (
STG1.ACCT_TYP_DSC <> TRGT.ACCT_TYP_DSC
);
DELETE FROM DWB_ACCT_class WHERE (ACCT_TYP_CD) IN (SELECT ACCT_TYP_CD FROM DWB_ACCT_CLASS_STG2 STG2);
INSERT INTO DWB_ACCT_CLASS SELECT * FROM DWB_ACCT_CLASS_STG2;

I think you don't even need staging table DWB_ACCT_CLSS_STG2. You can use the following MERGE command:
MERGE INTO DWB_ACCT_CLASS TRGT
USING (SELECT STG1.ACCT_CLSS_CD,
STG1.ACCT_CLSS_NME,
STG1.ACCT_CLSS_DSC,
STG1.PROCESS_NAME,
STG1.EXECUTION_ID,
STG1.FILE_ID,
STG1.FILE_DATE,
STG1.DATA_DATE,
STG1.LOAD_USER,
STG1.LOAD_DATE,
STG1.DW_LAST_UPDATE_TIME
FROM DWB_ACCT_CLSS_STG1 STG1) STG1
ON (STG1.ACCT_TYP_CD = TRGT.ACCT_TYP_CD
AND STG1.ACCT_TYP_DSC <> TRGT.ACCT_TYP_DSC)
WHEN MATCHED THEN
UPDATE SET TRGT.ACCT_CLSS_CD = STG1.ACCT_CLSS_CD, ..... -- EXCEPT ACCT_TYP_CD AND ACCT_TYP_DSC COLUMN
WHEN NOT MATCHED THEN
INSERT (... COLUMNS OF THE DWB_ACCT_CLASS TABLE ....)
VALUES (STG1.ACCT_CLSS_CD,STG1.ACCT_CLSS_NME,.....STG1.DW_LAST_UPDATE_TIME) -- ORDER MUST MATCH WITH THE INSERT CLASUE COLUMN LIST

Related

UPDATE from another table with multiple WHERE criteria

In Postgres 9.5, I want to connect to another DB using Postgres' dblink, get data and then use them to update another table.
-- connect to another DB, get data from table, put it in a WITH
WITH temp_table AS
(
SELECT r_id, descr, p_id
FROM
dblink('myconnection',
'SELECT
r_id, descr, p_id
FROM table
WHERE table.p_id
IN (10,20);'
)
AS tempTable(r_id integer, descr text, p_id integer)
)
-- now use temp_table to update
UPDATE anothertable
SET
descr =temp_table.descr
FROM anothertable AS x
INNER JOIN temp_table
ON
x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2) ;
dblink works fine and if I do select * from temp_table before the UPDATE, it has data.
The issue is the UPDATE itself. It runs with no errors, but it never actually updates the table.
I tried changing the UPDATE to:
UPDATE anothertable
SET
descr =temp_table.descr
FROM anothertable AS x , temp_table
WHERE x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2) ;
Same as above: runs with no errors, but it never actually updates the table.
I also tried to change the UPDATE to:
UPDATE anothertable
INNER JOIN temp_table
ON x.r_id = temp_table.r_id
AND
x.p_id = temp_table.p_id
AND
x.p_id IN (2)
SET descr =temp_table.descr
But I get:
ERROR: syntax error at or near "INNER" SQL state: 42601
Character: 1894
How can I fix this to actually update?
Don't repeat the target table in the FROM clause of the UPDATE:
WITH temp_table AS ( ... )
UPDATE anothertable x
SET descr = t.descr
FROM temp_table t
WHERE x.r_id = t.r_id
AND x.p_id = t.p_id
AND x.p_id IN (2);
Or simplified:
...
AND x.p_id = 2
AND t.p_id = 2
The manual:
Do not repeat the target table as a from_item unless you intend a self-join (in which case it must appear with an alias in the from_item).
Related:
UPDATE statement with multiple joins in PostgreSQL
SQL update query with substring WHERE clause

How to solve this SQL specific merge problem

How to solve the 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.
merge CARD_ALERTS as t
using #tblAlerts as s
on (t.Id = s.AlertId and t.CardId = s.CardId)
when not matched by target
then insert(Id, ExternalCodeHolder, CardId, IsCardOwner, IBAN, PAN, MinAmount, Currency, ByEmail, BySMS, IssueDate, IsActive)
values(s.AlertId, s.ExternalCodeHolder, s.CardId, s.IsCardOwner, s.IBAN, s.PAN, s.MinAmount, s.Currency, s.ByEmail, s.BySMS, getdate(), 1)
when matched
then update set t.ByEmail = s.ByEmail, t.BySMS = s.BySMS, IsActive = 1, t.MinAmount = s.MinAmount
when not matched by source and t.Id=#AlertId
then update set t.IsActive = 3
As explained in the error message and in the comments, multiple rows of the source table correspond to the same row in the target one.
This will show you which rows of the target table have multiple rows from the source table that try to update them:
select t.Id,t.CardId,count(*) as [count]
from CARD_ALERTS as t
inner join #tblAlerts as s
on (t.Id = s.AlertId and t.CardId = s.CardId)
group by t.Id,t.CardId
having count(*)>1
This will also show you the multiple rows of the source table:
select t.*,s.*
from CARD_ALERTS as t
inner join #tblAlerts as s on (t.Id = s.AlertId and t.CardId = s.CardId)
inner join
(
select ca.Id,ca.CardId
from CARD_ALERTS as ca
inner join #tblAlerts as s
on (ca.Id = s.AlertId and ca.CardId = s.CardId)
group by ca.Id,ca.CardId
having count(*)>1
)tkey on t.Id=tkey.Id and t.CardId=tkey.CardId

If the record exists in target but doesn't exist in source, i need to delete it

So, i have two tables, the target table and the source one. I need to delete the rows that exists in the target table, but doesn't exists in the source table.
And the code:
MERGE INTO (SELECT id_car_bk, car_brand_bk, car_type_bk, new_car
FROM car_catalog_backup) CB
USING (SELECT id_car, car_brand, car_type FROM car_catalog) C
ON (CB.id_car_bk = b.id_car)
WHEN NOT MATCHED THEN
INSERT
(CB.id_car_bk, CB.car_brand_bk, CB.car_type_bk)
VALUES
(C.id_car, C.car_brand, C.car_type)
WHEN MATCHED THEN
UPDATE SET CB.car_brand_bk = C.car_brand;
You can use
DELETE car_catalog_backup b
WHERE not exists
( SELECT 0
FROM car_catalog c
WHERE b.id_car_bk = c.id_car );
or
DELETE car_catalog_backup b
WHERE b.id_car_bk not in
( SELECT c.id_car
FROM car_catalog c );
assuming car_catalog is the source, and car_catalog_backup is the target. The First one is preferable, since it's more performant.
If your aim is to find out with a MERGE statement similar to your case, then use the following
MERGE INTO car_catalog_backup a
USING (SELECT id_car, car_brand, car_type, car_brand_bk
FROM car_catalog
JOIN car_catalog_backup
ON id_car_bk = id_car
) b
ON (a.id_car_bk = b.id_car)
WHEN MATCHED THEN
UPDATE SET a.new_car = 1
DELETE
WHERE a.car_brand_bk != b.car_brand
WHEN NOT MATCHED THEN
INSERT
(id_car_bk, car_brand_bk, car_type_bk)
VALUES
(b.id_car, b.car_brand, b.car_type)
to delete the records matched for id columns ( a.id_car_bk = b.id_car ) but not matched for brand code columns ( a.car_brand_bk != car_brand ) as an example.
Demo
Delete from target
Where not exists
(
Select 1
From source
Where join of source and target
)
With a left join:
DELETE target
FROM target LEFT JOIN source
ON target.someid = source.otherid
WHERE source.otherid IS NULL;

How in SQL UPDATE rows with differences only

I have two tables DESTINATION and STAGING with the same structure and very similar data. Only in few rows I've some differences. For example:
In DESTINATION table I have rows:
ID, Name, ShortName
1, Name1, N1
2, Name2, N2
3, Name3, N3
In STAGING table I have:
ID, Name, ShortName
1, Name1, N1
2, Name2, newN2
3, Name3, N3
How to update DESTINATION table only 2nd row with new data in ShortName column (newN2) and doing nothing with rows without any differences?
You can update like this
UPDATE A
SET A.shortname = B.ShortName
FROM DESTINATION A
INNER JOIN STAGING B
ON A.ID=B.ID
AND A.shortname<>B.ShortName;
Assuming you are using SQL Server, this is how you can do it:
update d
set ShortName = s.ShortName
from destination d
join staging s
on d.id = s.id
and d.ShortName != s.ShortName;
SQLFiddle: http://sqlfiddle.com/#!6/0119c/1
Try using an UPDATE...FROM:
UPDATE Destination
SET ShortName = stg.ShortName
FROM Staging stg -- Reference "staging" table
WHERE Destination.ID = stg.ID
AND Destination.ShortName <> stg.ShortName; -- Only update rows with "ShortName" values don't match
This will update your DESTINATION table using the data in your STAGING table. This works on Postgres.
http://www.sqlfiddle.com/#!17/ad370/1
In MS SQL , using merge script is best way of updating table. You can look up on multiple columns.
MERGE dbo.Destination AS TARGET
USING dbo.staging AS SOURCE
ON (TARGET.[id] = SOURCE.[id])
WHEN MATCHED
THEN
UPDATE
SET TARGET.[shortName] = SOURCE.[shortName]
WHEN NOT MATCHED BY TARGET
THEN
INSERT (
[Id]
,[Name]
,[shortName]
)
VALUES (
SOURCE.[Id]
,SOURCE.[Name]
,SOURCE.[shortName]
)

How to copy or update from one table to another table

I have two tables. user and user_new
the user contains the old data.
the user_new contains the new data.
I want to sync the user_new to user.
if the data exist in user_new and not exist in user,then insert to user.
if the data exist in user and user_new, then update.(compare with the column id)
what's the fast sql to do it?
This works on any server version -
-- 1) Insert new record
INSERT INTO old_table(id, column)
SELECT n.id, n.column
FROM new_table n
LEFT JOIN old_table o ON n.id = o.id
WHERE o.id IS NULL
-- 2) Update existed record
UPDATE o
SET column = n.column
FROM old_table o
JOIN new_table n ON n.id = o.id
From Sql Server 2008 onwards you can use Merge syntax
MERGE user target
USING user_new source
ON taget.ID = source.ID
WHEN MATCHED THEN
UPDATE
SET target.Column= source.Column1,target.column2=source.column2
WHEN NOT MATCHED BY TARGET THEN
INSERT (ID,Column1,Column2)
VALUES (source.ID,source.column1,source.column2);
or you can use the below query
INSERT INTO user(ID,column1,column2)
SELECT ID,column1,column2 FROM user_new AS source
WHERE NOT EXISTS (SELECT * FROM user WHERE ID = source.ID);
UPDATE target SET ...
FROM user AS target
INNER JOIN user_new AS source
ON target.ID = source.ID;
Sounds like you might need a Merge if you have sql server 2008+.
You can't do insert and update in a single query you have to do in seperate
select * from user where user_id not in (select user_new.user_id from user_new )
this query results the data for insert query similarly u have to update by replacing not in to in