it-swarm.com.de

Oracle: wie UPSERT (Update oder Einfügen in eine Tabelle?)

Die UPSERT-Operation aktualisiert oder fügt eine Zeile in eine Tabelle ein, je nachdem, ob die Tabelle bereits eine Zeile enthält, die den Daten entspricht:

if table t has a row exists that has key X:
    update t set mystuff... where mykey=X
else
    insert into t mystuff...

Was ist der beste Weg, dies zu tun, da Oracle keine spezifische UPSERT-Anweisung hat?

256
Mark Harrison

Eine Alternative zu MERGE (der "altmodische Weg"):

begin
   insert into t (mykey, mystuff) 
      values ('X', 123);
exception
   when dup_val_on_index then
      update t 
      set    mystuff = 123 
      where  mykey = 'X';
end;   
45
Tony Andrews

Die Anweisung MERGE führt Daten zwischen zwei Tabellen zusammen. Mit DUAL Können wir diesen Befehl verwenden. Beachten Sie, dass dies nicht gegen gleichzeitigen Zugriff geschützt ist.

create or replace
procedure ups(xa number)
as
begin
    merge into mergetest m using dual on (a = xa)
         when not matched then insert (a,b) values (xa,1)
             when matched then update set b = b+1;
end ups;
/
drop table mergetest;
create table mergetest(a number, b number);
call ups(10);
call ups(10);
call ups(20);
select * from mergetest;

A                      B
---------------------- ----------------------
10                     2
20                     1
195
Mark Harrison

Das doppelte Beispiel oben, das in PL/SQL enthalten ist, war großartig, weil ich etwas Ähnliches machen wollte, aber ich wollte es auf der Client-Seite haben.

MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name") 
    VALUES ( 2097153,"smith", "john" )

Aus einer C # -Perspektive ist dies jedoch langsamer als das Update durchzuführen und zu sehen, ob die betroffenen Zeilen 0 waren, und das Einfügen, wenn dies der Fall war.

93
MyDeveloperDay

Eine weitere Alternative ohne Ausnahmekontrolle:

UPDATE tablename
    SET val1 = in_val1,
        val2 = in_val2
    WHERE val3 = in_val3;

IF ( sql%rowcount = 0 )
    THEN
    INSERT INTO tablename
        VALUES (in_val1, in_val2, in_val3);
END IF;
44
Brian Schmitt
  1. einfügen, falls nicht vorhanden 
  2. aktualisieren:
     
 INSERT IN Mytable (id1, t1) 
 SELECT 11, 'x1' FROM DUAL 
 WHERE NOT EXISTS (SELECT id1 FROM mytble WHERE id1 = 11); 

 UPDATE mytable SET t1 = 'x1' WO ID1 = 11; 
22
test1

Keine der bisher gegebenen Antworten ist sicher vor gleichzeitigen Zugriffen, wie Tim Sylvester in seinem Kommentar hervorhebt, und wird bei Rennen Ausnahmen hervorrufen. Um dies zu beheben, muss die Einfüge-/Aktualisierungs-Kombination in eine Art Schleifenanweisung eingebunden werden, damit im Falle einer Ausnahme das Ganze erneut versucht wird.

Als Beispiel kann der Code von Grommit in eine Schleife eingebunden werden, um die Sicherheit zu gewährleisten, wenn er gleichzeitig ausgeführt wird:

PROCEDURE MyProc (
 ...
) IS
BEGIN
 LOOP
  BEGIN
    MERGE INTO Employee USING dual ON ( "id"=2097153 )
      WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
      WHEN NOT MATCHED THEN INSERT ("id","last","name") 
        VALUES ( 2097153,"smith", "john" );
    EXIT; -- success? -> exit loop
  EXCEPTION
    WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
      NULL; -- exception? -> no op, i.e. continue looping
    WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
      NULL; -- exception? -> no op, i.e. continue looping
  END;
 END LOOP;
END; 

N.B. Im Transaktionsmodus SERIALIZABLE, den ich btw nicht empfehlen kann, laufen Sie möglicherweise in ORA-08177: Der Zugriff für diese Transaction -Ausnahmen kann nicht serialisiert werden.

22

Ich würde gerne Grommit antworten, es sei denn, es werden Dupewerte benötigt. Ich habe eine Lösung gefunden, in der sie einmal angezeigt werden kann: http://forums.devshed.com/showpost.php?p=1182653&postcount=2

MERGE INTO KBS.NUFUS_MUHTARLIK B
USING (
    SELECT '028-01' CILT, '25' SAYFA, '6' KUTUK, '46603404838' MERNIS_NO
    FROM DUAL
) E
ON (B.MERNIS_NO = E.MERNIS_NO)
WHEN MATCHED THEN
    UPDATE SET B.CILT = E.CILT, B.SAYFA = E.SAYFA, B.KUTUK = E.KUTUK
WHEN NOT MATCHED THEN
    INSERT (  CILT,   SAYFA,   KUTUK,   MERNIS_NO)
    VALUES (E.CILT, E.SAYFA, E.KUTUK, E.MERNIS_NO); 
18
Hubbitus

Ein Hinweis zu den beiden Lösungen, die Folgendes vorschlagen:

1) Einfügen, wenn Ausnahme dann aktualisieren,

oder

2) Aktualisieren, wenn sql% rowcount = 0, dann einfügen

Die Frage, ob zuerst eingefügt oder aktualisiert werden soll, hängt auch von der Anwendung ab. Erwarten Sie weitere Einfügungen oder mehr Updates? Diejenige, die am erfolgreichsten ist, sollte zuerst gehen. 

Wenn Sie sich für das falsche entscheiden, erhalten Sie eine Reihe unnötiger Indexablesungen. Kein riesiger Deal, aber trotzdem etwas zu bedenken.

8
AnthonyVO

Ich benutze das erste Codebeispiel seit Jahren. Bemerken Sie nicht gefunden, anstatt zu zählen.

UPDATE tablename SET val1 = in_val1, val2 = in_val2
    WHERE val3 = in_val3;
IF ( sql%notfound ) THEN
    INSERT INTO tablename
        VALUES (in_val1, in_val2, in_val3);
END IF;

Der folgende Code ist der möglicherweise neue und verbesserte Code

MERGE INTO tablename USING dual ON ( val3 = in_val3 )
WHEN MATCHED THEN UPDATE SET val1 = in_val1, val2 = in_val2
WHEN NOT MATCHED THEN INSERT 
    VALUES (in_val1, in_val2, in_val3)

Im ersten Beispiel führt das Update eine Indexsuche durch. Es muss, um die rechte Zeile zu aktualisieren. Oracle öffnet einen impliziten Cursor, und wir verwenden ihn, um eine entsprechende Einfügung einzuhüllen, sodass wir wissen, dass die Einfügung nur dann erfolgt, wenn der Schlüssel nicht vorhanden ist. Das Einfügen ist jedoch ein unabhängiger Befehl, und es muss eine zweite Suche ausgeführt werden. Ich kenne die inneren Abläufe des Zusammenführungsbefehls nicht. Da es sich bei dem Befehl jedoch um eine einzelne Einheit handelt, könnte Oracle die korrekte Einfügung oder Aktualisierung mit einer einzigen Indexsuche ausführen lassen.

Ich denke, das Zusammenführen ist besser, wenn Sie einige Verarbeitungsschritte ausführen müssen. Dazu müssen Sie Daten aus einigen Tabellen übernehmen, eine Tabelle aktualisieren und möglicherweise Zeilen einfügen oder löschen. Für den einzeiligen Fall können Sie jedoch den ersten Fall in Betracht ziehen, da die Syntax häufiger ist.

7

Kopieren und Einfügen eines Beispiels, um mit MERGE eine Tabelle in eine andere umzuwandeln:

CREATE GLOBAL TEMPORARY TABLE t1
    (id VARCHAR2(5) ,
     value VARCHAR2(5),
     value2 VARCHAR2(5)
     )
  ON COMMIT DELETE ROWS;

CREATE GLOBAL TEMPORARY TABLE t2
    (id VARCHAR2(5) ,
     value VARCHAR2(5),
     value2 VARCHAR2(5))
  ON COMMIT DELETE ROWS;
ALTER TABLE t2 ADD CONSTRAINT PK_LKP_MIGRATION_INFO PRIMARY KEY (id);

insert into t1 values ('a','1','1');
insert into t1 values ('b','4','5');
insert into t2 values ('b','2','2');
insert into t2 values ('c','3','3');


merge into t2
using t1
on (t1.id = t2.id) 
when matched then 
  update set t2.value = t1.value,
  t2.value2 = t1.value2
when not matched then
  insert (t2.id, t2.value, t2.value2)  
  values(t1.id, t1.value, t1.value2);

select * from t2

Ergebnis:

  1. b 4 5 
  2. c 3 3 
  3. a 1 1
0
Bechyňák Petr