Logo Oracle Deutschland   Application Express Community
SQL Insert, Update und Delete in einem: Das MERGE-Kommando
Erscheinungsmonat APEX-Version Datenbankversion
November 2011 alle 9.2

Wie alle anderen Entwickler kommt auch der APEX-Entwickler immer wieder in die Verlegenheit, eine oder mehrere Zeilen in eine Tabelle "hineinzumischen". Wenn die Zeile also schon da ist, soll ein SQL UPDATE stattfinden, wenn sie noch nicht da ist, wird ein INSERT gemacht. Vielfach hilft man sich dann mit PL/SQL-Prozeduren, die zuerst mal einen INSERT versuchen, und wenn ein Fehler auftritt (DUP_VAL_ON_INDEX) ein UPDATE machen. Das geht aber auch eleganter: mit dem MERGE-Kommando. Dieses gibt es bereits seit Oracle9i - und in Oracle10g wurde es nochmals erweitert. Es wurde zwar vor allem für Ladeprozesse in Datawarehouse-Umgebungen entwickelt, dadurch ist es für APEX aber nicht weniger nützlich. So ist MERGE bspw. hervorragend geeignet, um die Inhalte einer APEX-Collection in eine Tabelle zu übertragen. In diesem Tipp wird das MERGE-Kommando und seine Anwendung vorgestellt.

Als Beispiel dient die bekannte Tabelle EMP. In einer zusätzlichen Tabelle EMP_NEW sind bereits einige Zeilen enthalten; und die Inhalte aus der Tabelle EMP sollen dort hineingemischt werden.

merge into emp_new dest
using (select * from emp) src
on (src.empno = dest.empno)
when matched then update set 
  dest.ename    = src.ename,
  dest.sal      = src.sal,
  dest.hiredate = src.hiredate,
  dest.job      = src.job,
  dest.deptno   = src.deptno,
  dest.comm     = src.comm,
  dest.mgr      = src.mgr
when not matched then 
  insert (empno, ename, sal, comm, mgr, job, hiredate, deptno)
  values (src.empno, src.ename, src.sal, src.comm, src.mgr, src.job, src.hiredate, src.deptno)
/

Das Kommando ist eigentlich selbsterklärend: Man kann sehr schön die einzelnen Abschnitte erkennen:

  • MERGE INTO ... gibt das Ziel der MERGE-Operation an (typischerweise eine Tabelle)
  • USING ... gibt die Datenquelle an; das kann eine Tabelle, eine View oder eine SELECT-Abfrage, welche Literale "from DUAL" selektiert, sein.
  • ON (matching-kriterium) legt fest, wie bereits vorhandene Zeilen erkannt werden sollen - hier anhand der EMPNO. Aber auch komplerere Kriterien sind möglich.
  • WHEN MATCHED ... legt fest, was passieren soll, wenn die Zeile schon da ist; in diesem Fall soll ein SQL UPDATE erfolgen; die Spalteninhalte der Zieltabelle sollen mit denen der Quelldaten überschrieben werden.
  • WHEN NOT MATCHED ... legt fest, was passieren soll, wenn die Zeile nicht vorhanden ist; in diesem Fall findet ein SQL INSERT statt - die Zeile wird anhand der Quelldaten erzeugt.

MERGE kann jedoch ab Oracle10g noch mehr ... So sollen die Zeilen der DEPTNO 30 nicht mehr in die Zieltabelle gemischt werden. Das sieht dann wie folgt aus:

merge into emp_new dest
using (select * from emp) src
on (src.empno = dest.empno)
when matched then update set 
  dest.ename    = src.ename,
  dest.sal      = src.sal,
  dest.hiredate = src.hiredate,
  dest.job      = src.job,
  dest.deptno   = src.deptno,
  dest.comm     = src.comm,
  dest.mgr      = src.mgr
  -- ONLY IF DEPTNO IN SOURCE DATA NOT EQUALS "30"
  where not src.deptno = 30
when not matched then 
  insert (empno, ename, sal, comm, mgr, job, hiredate, deptno)
  values (src.empno, src.ename, src.sal, src.comm, src.mgr, src.job, src.hiredate, src.deptno)
  -- ONLY IF DEPTNO IN SOURCE DATA NOT EQUALS "30"
  where not src.deptno = 30
/

Mit Hilfe von SQL MERGE kann man quasi eine WHERE-Klausel an eine Einfügeoperation hängen. Alle Zeilen mit DEPTNO = 30 werden also nicht aus EMP an EMP_NEW übertragen. Aber die Zeilen, die bereits vor der MERGE-Operation existierten, sind immer noch da. Der nächste Schritt wäre nun, die Zeile in der Zieltabelle zu löschenx, wenn in der Quelltabelle die DEPTNO auf "30" gesetzt ist. Und MERGE kann (ab Oracle10g) auch das - mit der DELETE-Klausel:

merge into emp_new dest
using (select * from emp) src
on (src.empno = dest.empno)
when matched then update set 
  dest.ename    = src.ename,
  dest.sal      = src.sal,
  dest.hiredate = src.hiredate,
  dest.job      = src.job,
  dest.deptno   = src.deptno,
  dest.comm     = src.comm,
  dest.mgr      = src.mgr
  -- DELETE ROWS IN TARGET TABLE IF SOURCE DEPTNO EQUALS 30
  delete where deptno = 30
when not matched then 
  insert (empno, ename, sal, comm, mgr, job, hiredate, deptno)
  values (src.empno, src.ename, src.sal, src.comm, src.mgr, src.job, src.hiredate, src.deptno)
  -- ONLY IF DEPTNO IN SOURCE DATA NOT EQUALS "30"
  where not src.deptno = 30
/

So kann man in einem Ladeprozeß auch Zeilen mit einem "Löschflag" versehen und mit MERGE dann gezielt löschen. Wie man sieht, ist MERGE ein sehr mächtiges Kommando - viele Ladevorgänge, aber auch Einzelsatzoperationen, lassen sich so mit einem einzigen SQL-Kommando erledigen - und das ist effizienter und leichter wartbar, als selbstprogrammierte PL/SQL-Schleifenkonstrukte. Und Operationen auf größeren Datenbeständen sind mit MERGE nahezu immer schneller als mit PL/SQL-Konstrukten. Mehr Informationen finden sich in der Dokumentation.

Zurück zur Community-Seite