MERGE and DML RETURNING clause in Oracle 23ai

For a long time, we all “knew” that MERGE did not support a RETURNING clause. You can ask ChatGPT, Gemini, or Google, and the answer would still be the same: Nope, no luck! LLMs respond with confidence, and Google’s top results show various workarounds for the problem. I swear I saw the lifting of this restriction in the New Features Guide back when the version was still called 23c :-). Since I’m currently preparing a conference talk where this is relevant, I naturally wanted to check whether my memory was just playing tricks on me — or if it’s actually true.

TL;DR:

Yes, Oracle 23ai now supports MERGE … RETURNING. ChatGPT and Gemini still deny it, even inventing fake error messages. But the Oracle 23ai documentation quietly confirms it — and real tests prove it works, including OLD and NEW value support.

A Word of Caution

In today’s fast-paced world, everyone wants to save time and optimize effort. As an Oracle developer, why dig through AskTom, Oracle Docs, or StackOverflow when ChatGPT gives you a confident answer, complete with examples?

Having used LLMs a lot in recent weeks and months, I’m impressed by their progress. But despite improvements, hallucinations are still very much a thing — and some are tricky to spot. You need to know your stuff.

Both ChatGPT and Google Gemini insist that MERGE with RETURNING is impossible. ChatGPT even invents a fictitious error message — ORA-38911 — which does exist, but has nothing to do with MERGE or RETURNING.

Figure 1: ChatGPT with fictitious error message

Figure 2: Also Google’s LLM doesn’t know about new feature

I’m not saying don’t use LLMs — just always double-check the answers.

A Hard-to-Find Feature

Next stop: Oracle documentation. And yes, you really have to look twice. In the New Features Guide, the section is called SQL UPDATE RETURN clause enhancements, and MERGE is just mentioned casually — as if it had always supported RETURNING.

Figure 3: MERGE casually dropped in under “new/old values” enhancements

Looking at the MERGE documentation page, you’ll find the RETURNING clause shown in the syntax railroad diagram — but not a single word about it in the actual text!

Curious, I wondered whether I had missed this feature already being added in Oracle 21c? A quick check confirms: nope. Back then, RETURNING was only documented for INSERT, UPDATE AND DELETE. Digging deeper into the 23ai docs, the same section from “Database PL/SQL Language Reference” documents the usage with MERGE!

Test

Let’s try it out to be sure. Listing 1 shows that the RETURNING clause works with MERGE, and that the 23ai enhancements like OLD and NEW values are fully functional.

SQL> SELECT * FROM emp_sal_increase

    DEPTNO INCREASE_PCT
---------- ------------
        20           20
1 row selected.

SQL> DECLARE 
TYPE t_sal_tab IS TABLE OF emp.sal%TYPE;
TYPE t_empno_tab IS TABLE OF emp.empno%TYPE;
l_empno t_empno_tab;
l_salo t_sal_tab;
l_saln t_sal_tab;
BEGIN 
    MERGE INTO emp t
    USING  emp_sal_increase  q
    ON    (t.deptno = q.deptno)
    WHEN MATCHED THEN UPDATE SET t.sal=t.sal*(1+q.increase_pct/100)
    RETURNING empno, OLD sal, NEW sal BULK COLLECT INTO l_empno, l_salo, l_saln;
   
    FOR i IN l_salo.first .. l_salo.last LOOP
      DBMS_OUTPUT.put_line('EMPNO=' || l_empno(i)|| ', SAL changed from '||l_salo(i) ||' to ' ||l_saln(i));
    END LOOP;
END;

PL/SQL procedure successfully completed.

EMPNO=7369, SAL changed from 800 to 960
EMPNO=7566, SAL changed from 2975 to 3570
EMPNO=7788, SAL changed from 3000 to 3600
EMPNO=7876, SAL changed from 1100 to 1320
EMPNO=7902, SAL changed from 3000 to 3600

Listing 1: Returning clause works for MERGE in 23ai

Listing 1 shows BULK COLLECT for multiple rows, but it works with scalar values too — even aggregate functions, as shown in Listing 2.

SQL> DECLARE 
v_old_avg NUMBER;
v_new_avg NUMBER;
BEGIN 
    MERGE INTO emp t
    USING  emp_sal_increase  q
    ON    (t.deptno = q.deptno)
    WHEN MATCHED THEN UPDATE SET t.sal=t.sal*(1+q.increase_pct/100)
    RETURNING AVG(old sal), AVG(new sal)  INTO v_old_avg, v_new_avg;
    
    DBMS_OUTPUT.put_line('Average SAL changed from '||v_old_avg ||' to ' ||v_new_avg);
    
END;
PL/SQL procedure successfully completed.

Average SAL changed from 2175 to 2610

Listing 2: Returning with scalar values

Conclusion

So yes, MERGE … RETURNING now works in Oracle 23ai — including with OLD and NEW values. It’s real, it’s documented (somewhere), and can be quite useful.

But Oracle, if you’re listening: could we maybe highlight this a bit more clearly in the documentation?
This was a long-awaited feature — many developers have been hoping for it for years. No need to hide it in a syntax diagram. Give it the spotlight it deserves!

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *