Usually there are cases when we need to perform an update or insert into the same table using two different queries. I wanted to see if this could be done on a table using a merge statement.
All I want to know if this can be done or not. Otherwise I'll have to stick with separating the query back into an update/insert operation separately.
Here is what I have so far:
METHOD 1:
MERGE INTO TABLEA TARGET
USING (
SELECT 1 FROM DUAL
) SOURCE
ON (TARGET.TARGET.COLA = '001'
AND TARGET.TARGET.COLB = '1111111'
AND TARGET.COLC = '201302'
)
WHEN MATCHED THEN
UPDATE SET TARGET.COLA = '001'
,TARGET.COLB = '1111111'
,TARGET.COLC = '201304'
,TARGET.CREATEDATE = SYSDATE
,TARGET.USERID = 'USERA'
WHEN NOT MATCHED THEN
INSERT (TARGET.COLA
,TARGET.COLB
,TARGET.COLC
,TARGET.COLD
,TARGET.CREATEDATE
,TARGET.USERID)
VALUES('001'
,'1111111'
,'201304'
,'123'
,SYSDATE
,'USERA')
At first this method made sense to me, because I would always be returning results from the source, and I'd update and insert accordingly. However, oracle refuses to follow this:
SQL Error: ORA-38104: Columns referenced in the ON Clause cannot be updated: "TARGET"."EFF_FISCAL_YR_PD_NBR" 38104. 00000 - "Columns referenced in the ON Clause cannot be updated: %s" *Cause: LHS of UPDATE SET contains the columns referenced in the ON Clause
METHOD 2:
MERGE INTO TABLEA TARGET
USING (
SELECT ROWID AS RID,COLA,COLB,COLC
FROM TABLEA
WHERE COLA = '001'
AND COLB = '1111111'
AND COLC = '201301'
) SOURCE
ON (TARGET.ROWID = SOURCE.RID)
WHEN MATCHED THEN
UPDATE SET TARGET.COLA = '001'
,TARGET.COLB = '1111111'
,TARGET.COLC = '201304'
,TARGET.CREATEDATE = SYSDATE
,TARGET.USERID = 'USERA'
WHEN NOT MATCHED THEN
INSERT (TARGET.COLA
,TARGET.COLB
,TARGET.COLC
,TARGET.COLD
,TARGET.CREATEDATE
,TARGET.USERID)
VALUES('001'
,'1111111'
,'201304'
,'123'
,SYSDATE
,'USERA')
The logic behind this is, if I try to look up values from the source table, and it matches, it'll find the records and update itself with those values. However, the issue comes when trying to insert if it doesn't match. Because the source is filtered, no records get returned, therefore there's nothing for the target to match on, and nothing gets inserted. What I would like this to do is insert if no record is found in the SOURCE (implicitly not matched against the target), especially since the insert statement contains nothing by values passed in from variables rather than the source itself.
I've tried updating the source to look like this:
SELECT ROWID AS RID,COLA,COLB,COLC
FROM TABLEA
WHERE COLA = '001'
AND COLB = '1111111'
AND COLC = '201301'
UNION ALL
SELECT ROWID,NULL,NULL,NULL FROM DUAL
But the problem with this is that the merge does the update on the record it matched AND an insert on the record it didn't match.
For those of you who want to know why I'm using a ROWID. This is because the design (not by me) indicated that COLA and COLB would be combined primary key that would be used as an index on the table. Duplicates of COLA, COLB, and COLC are not allowed but they are all updateable via the front end interface. I understand the pitfalls of ROWID, but because I'm only working with one table as target and source, regardless of any CRUD operations I perform on the table, the ROWID will always be matched onto itself.
Summary: I got the self merge to work only when performing an update on a matching item, but inserting doesn't work.