Skip to Main Content
  • Questions
  • Merge - unfold records based on conditional join

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Prashanth.

Asked: November 02, 2017 - 9:40 am UTC

Last updated: November 02, 2017 - 11:10 am UTC

Version: 12c

Viewed 1000+ times

You Asked

Hi Team,

Need your help or suggestion on altering a merge statement.

I have a below staging table A_TRANSACTION_STAGING which gets merged to main table A_TRANSACTION :

A_TRANSACTION_STAGING :
TRANSACTION_ID  NUMBER
REGION_CD       VARCHAR2(5)
CHANNEL         VARCHAR2(15)
GROUP           VARCHAR2(15)
MATERIAL        VARCHAR2(20)
PRICE           DECIMAL(10,2)
COUNTRY         VARCHAR2(15)

A_TRANSACTION :
TRANSACTION_ID  NUMBER
REGION_CD       VARCHAR2(5)
CHANNEL         VARCHAR2(15)
GROUP           VARCHAR2(15)
MATERIAL        VARCHAR2(20)
PRICE           DECIMAL(10,2)
CREATED_DATE    DATE
MODIFIED_DATE   DATE

Merge query :

MERGE INTO A_TRANSACTION tgt
USING (SELECT TRANSACTION_ID, REGION_CD, CHANNEL, GROUP, MATERIAL, PRICE, SYSDATE AS CREATED_DATE, SYSDATE AS MODIFIED_DATE
              FROM  (
                     SELECT TRANSACTION_ID, REGION_CD, CHANNEL, GROUP, MATERIAL, PRICE, ROW_NUMBER() over (partition by TRANSACTION_ID, REGION_CD, CHANNEL, GROUP, MATERIAL order by rowid desc ) last_rec FROM A_TRANSACTION_STAGING) WHERE last_rec = 1
       ) src
ON (tgt.TRANSACTION_ID = src.TRANSACTION_ID AND tgt.REGION_CD = src.REGION_CD AND tgt.CHANNEL = src.CHANNEL AND tgt.GROUP = src.GROUP AND tgt.MATERIAL = src.MATERIAL)
WHEN matched THEN
  UPDATE SET tgt.PRICE = src.PRICE,
             tgt.modified_date = src.modified_date
WHEN NOT matched THEN
  INSERT (tgt.TRANSACTION_ID,
          tgt.REGION_CD,
          tgt.CHANNEL,
          tgt.GROUP,
          tgt.MATERIAL,
          tgt.PRICE,
          tgt.CREATED_DATE,
          tgt.MODIFIED_DATE)
  VALUES (src.TRANSACTION_ID,
          src.REGION_CD,
          src.CHANNEL,
          src.GROUP,
          src.MATERIAL,
          src.PRICE,
          src.CREATED_DATE,
          src.MODIFIED_DATE);


Now the requirement is to unfold the records in staging table based on a conditional join of table B_RULE

B_RULE :
REGION_CD    VARCHAR2(5)
CHANNEL      VARCHAR2(15)
GROUP        VARCHAR2(15)

Ex:

If A_TRANSACTION_STAGING records are :

TRANSACTION_ID|REGION_CD|CHANNEL|GROUP|MATERIAL|PRICE|COUNTRY|
158963|AMR|CUSTOMER|ONLINE|PMGHJTE|125|USA|
158964|EUR| | |OTGHYW|120|IRL|


If B_RULE records are :

REGION_CD|COUNTRY|CHANNEL|GROUP|
EUR|IRL|CUSTOMER|ONLINE|
EUR|IRL|EDUCATION|RESELLER|


Since REGION_CD value in A_TRANSACTION_STAGING is 'EUR' and COUNTRY is 'IRL' which is present in B_RULE table, i need to use all the CHANNEL & GROUP values in B_RULE while updating A_TRANSACTION . If not present in B_RULE, it should get merged as it is.

Above records should get merged into A_TRANSACTION as :

TRANSACTION_ID|REGION_CD|CHANNEL|GROUP|MATERIAL|PRICE|CREATED_DATE|MODIFIED_DATE
158963|AMR|CUSTOMER|ONLINE|PMGHJTE|125|01-NOV-2017|01-NOV-2017
158964|EUR|CUSTOMER|ONLINE|OTGHYW|120|01-OCT-2017|01-NOV-2017
158964|EUR|EDUCATION|RESELLER|OTGHYW|120|01-OCT-2017|01-NOV-2017


Further, i also need to skip insert for B_RULE matched records. It should only update the available records.
Is this possible to be handled in a single merge or should it be handled in a custom procedure.

Thanks.

and Chris said...

So it looks to me like you just need to outer join b_rule to the working table in the using clause of merge:

create table A_TRANSACTION_STAGING (
TRANSACTION_ID  NUMBER,
REGION_CD       VARCHAR2(5),
CHANNEL         VARCHAR2(15),
GRP             VARCHAR2(15),
MATERIAL        VARCHAR2(20),
PRICE           DECIMAL(10,2),
COUNTRY         VARCHAR2(15)
);

create table A_TRANSACTION (
TRANSACTION_ID  NUMBER,
REGION_CD       VARCHAR2(5),
CHANNEL         VARCHAR2(15),
GRP             VARCHAR2(15),
MATERIAL        VARCHAR2(20),
PRICE           DECIMAL(10,2),
CREATED_DATE    DATE,
MODIFIED_DATE   DATE
);

create table B_RULE (
REGION_CD    VARCHAR2(5),
COUNTRY      VARCHAR2(3),
CHANNEL      VARCHAR2(15),
GRP          VARCHAR2(15)
);

insert into A_TRANSACTION_STAGING values (
158963,'AMR','CUSTOMER','ONLINE','PMGHJTE','125','USA'
);
insert into A_TRANSACTION_STAGING values (
158964,'EUR',null,null,'OTGHYW','120','IRL'
);

insert into b_rule values ('EUR','IRL','CUSTOMER','ONLINE');
insert into b_rule values ('EUR','IRL','EDUCATION','RESELLER');


MERGE INTO A_TRANSACTION tgt
USING (select a.transaction_id, 
       a.material,
       a.price,
       coalesce(b.country, a.country) country,
       coalesce(b.region_cd, a.region_cd) region_cd,
       coalesce(b.channel, a.channel) channel,
       coalesce(b.grp, a.grp) grp,
       sysdate as created_date, sysdate as modified_date
from   a_transaction_staging a
left join b_rule b
on    a.country = b.country
and   b.region_cd = b.region_cd
      ) src
ON (tgt.TRANSACTION_ID = src.TRANSACTION_ID AND tgt.REGION_CD = src.REGION_CD AND tgt.CHANNEL = src.CHANNEL AND tgt.GRP = src.GRP AND tgt.MATERIAL = src.MATERIAL)
WHEN matched THEN
  UPDATE SET tgt.PRICE = src.PRICE,
             tgt.modified_date = src.modified_date
WHEN NOT matched THEN
  INSERT (tgt.TRANSACTION_ID,
          tgt.REGION_CD,
          tgt.CHANNEL,
          tgt.GRP,
          tgt.MATERIAL,
          tgt.PRICE,
          tgt.CREATED_DATE,
          tgt.MODIFIED_DATE)
  VALUES (src.TRANSACTION_ID,
          src.REGION_CD,
          src.CHANNEL,
          src.GRP,
          src.MATERIAL,
          src.PRICE,
          src.CREATED_DATE,
          src.MODIFIED_DATE);
          
select * from A_TRANSACTION;

TRANSACTION_ID   REGION_CD   CHANNEL     GRP        MATERIAL   PRICE   CREATED_DATE           MODIFIED_DATE          
          158963 AMR         CUSTOMER    ONLINE     PMGHJTE        125 02-NOV-2017 04:08:39   02-NOV-2017 04:08:39   
          158964 EUR         CUSTOMER    ONLINE     OTGHYW         120 02-NOV-2017 04:08:39   02-NOV-2017 04:08:39   
          158964 EUR         EDUCATION   RESELLER   OTGHYW         120 02-NOV-2017 04:08:39   02-NOV-2017 04:08:39 


I'm not sure what you mean by:

Further, i also need to skip insert for B_RULE matched records. It should only update the available records.

But you can add where clauses to the when (not) matched clauses. So you can use this to stop inserting or updating rows as needed.

PS - please supply your tables and data in the form of "create table" and "insert into". Not only does this mean we can get an answer to you quicker, we're less likely to make a mistake building your test case.

Is this answer out of date? If it is, please let us know via a Comment

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.