Frage

Ich muß in die Lage, eine Oracle-Abfrage ausführen, die eine Anzahl von Zeilen einzufügen geht, aber es überprüft auch, ob ein Primärschlüssel vorhanden ist, um zu sehen, und wenn dies der Fall, dann springt es, dass die Einlage. So etwas wie:

INSERT ALL
    IF NOT EXISTS( SELECT 1 WHERE fo.primary_key='bar' )
    (
        INSERT INTO 
            schema.myFoo fo ( primary_key, value1, value2 )
        VALUES
            ('bar','baz','bat')
    ),

    IF NOT EXISTS( SELECT 1 WHERE fo.primary_key='bar1' )
    (
        INSERT INTO 
            schema.myFoo fo ( primary_key, value1, value2 )
        VALUES
            ('bar1','baz1','bat1')
    )
SELECT * FROM schema.myFoo;

Ist das überhaupt möglich mit Oracle?

Bonuspunkte, wenn Sie mir sagen, wie dies in PostgreSQL oder MySQL zu tun.

War es hilfreich?

Lösung

Die Anweisung wird MERGE genannt. Schauen Sie es oben, ich bin zu faul.

Beachten Sie aber, dass MERGE nicht atomar ist, welche den folgenden Effekt verursachen könnte (danke, Marius):

SESS1:

create table t1 (pk int primary key, i int);
create table t11 (pk int primary key, i int);
insert into t1 values(1, 1);
insert into t11 values(2, 21);
insert into t11 values(3, 31);
commit;

SESS2: insert into t1 values(2, 2);

SESS1:

MERGE INTO t1 d
USING t11 s ON (d.pk = s.pk)
WHEN NOT MATCHED THEN INSERT (d.pk, d.i) VALUES (s.pk, s.i);

SESS2: commit;

SESS1: ORA-00001

Andere Tipps

Kommen spät zur Party, aber ...

Mit Oracle 11.2.0.1 gibt es einen semantischen Hinweis , die dies tun können: IGNORE_ROW_ON_DUPKEY_INDEX

Beispiel:

insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(customer_orders,pk_customer_orders) */
  into customer_orders
       (order_id, customer, product)
values (    1234,     9876,  'K598')
     ;

UPDATE : Obwohl dieser Hinweis funktioniert (wenn Sie es richtig buchstabieren), gibt es bessere Ansätze die Oracle 11R2 nicht benötigen:

Erste Ansatz-direkte Übersetzung von oben semantischem Hinweis:

begin
  insert into customer_orders
         (order_id, customer, product)
  values (    1234,     9876,  'K698')
  ;
  commit;
exception
  when DUP_VAL_ON_INDEX
  then ROLLBACK;
end;

Second aproach-a Los schneller als die beiden oben genannten Hinweise, wenn eine Menge Streit gibt es:

begin
    select count (*)
    into   l_is_matching_row
    from   customer_orders
    where  order_id = 1234
    ;

    if (l_is_matching_row = 0)
    then
      insert into customer_orders
             (order_id, customer, product)
      values (    1234,     9876,  'K698')
      ;
      commit;
    end if;
exception
  when DUP_VAL_ON_INDEX
  then ROLLBACK;
end;

Dies ist nur Einsätze, wenn das Element eingeführt werden soll, nicht bereits vorhanden ist.

Funktioniert wie:

if not exists (...) insert ... 

in T-SQL

insert into destination (DESTINATIONABBREV) 
  select 'xyz' from dual 
  left outer join destination d on d.destinationabbrev = 'xyz' 
  where d.destinationid is null;

ist vielleicht nicht schön, aber es ist praktisch:)

Wir können die DUAL kombinieren und NICHT VORHANDEN Ihre Anforderung zu archivieren:

INSERT INTO schema.myFoo ( 
    primary_key, value1, value2
) 
SELECT
    'bar', 'baz', 'bat' 
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 
    FROM schema.myFoo
    WHERE primary_key = 'bar'
);

Wenn Sie nicht von einer anderen Tabelle verschmelzen in mögen, sondern neue Daten einfügen ... ich kam mit dieser. Gibt es vielleicht einen besseren Weg, dies zu tun?

MERGE INTO TABLE1 a
    USING DUAL
    ON (a.C1_pk= 6)
WHEN NOT MATCHED THEN
    INSERT(C1_pk, C2,C3,C4)
    VALUES (6, 1,0,1);

Es wird dieser Code auf dem Client dann haben Sie viele Fahrten an den Server, so dass zu beseitigen.

Fügen Sie alle Daten in eine Tabelle temportary sagen T mit der gleichen Struktur wie myFoo

Dann

insert myFoo
  select *
     from t
       where t.primary_key not in ( select primary_key from myFoo) 

Dies sollte auch auf andere Datenbanken arbeiten - ich habe dies auf Sybase getan

Es ist nicht das beste, wenn nur sehr wenige der neuen Daten eingefügt werden, da Sie alle Daten über den Draht kopiert haben.

DECLARE
   tmp NUMBER(3,1);
BEGIN
  SELECT COUNT(content_id) INTO tmp FROM contents WHERE (condition);
  if tmp != 0 then
    INSERT INTO contents VALUES (...);
  else
    INSERT INTO contents VALUES (...);
  end if;
END;

habe ich den Code oben. Es ist lang, aber, einfach und funktionierte für mich. Ähnliche, zu Micheal-Code.

Dies ist eine Antwort auf den Kommentar von erikkallen geschrieben:

  

Sie brauchen nicht eine temporäre Tabelle. Wenn du   nur ein paar Zeilen, (SELECT 1 FROM   Dual UNION SELECT FROM 2 dual) wird   machen. Warum sollte Ihr Beispiel geben   ORA-0001? Würde nicht fusionieren nehmen die   Update-Sperre auf dem Indexschlüssel und nicht   weiter, bis Sess1 hat entweder   festgeschrieben oder zurückgesetzt? - erikkallen

Nun, versuchen Sie es selbst und sagen Sie mir, ob Sie die gleichen Fehler oder nicht:

SESS1:

create table t1 (pk int primary key, i int);
create table t11 (pk int primary key, i int);
insert into t1 values(1, 1);
insert into t11 values(2, 21);
insert into t11 values(3, 31);
commit;

SESS2: insert into t1 values(2, 2);

SESS1:

MERGE INTO t1 d
USING t11 s ON (d.pk = s.pk)
WHEN NOT MATCHED THEN INSERT (d.pk, d.i) VALUES (s.pk, s.i);

SESS2: commit;

SESS1: ORA-00001

Wenn Ihre Tabelle „unabhängig“ von den anderen ist (ich meine, es wird nicht eine Kaskade löschen auslösen oder werden keine Fremdschlüssel Beziehungen auf null gesetzt), könnte ein netter Trick sein, zuerst die Zeile zu löschen und es dann wieder INSERT . Es könnte so gehen:

FROM MyTable DELETE WHERE PROP1 = 'aaa'; // vorausgesetzt, es wählt höchstens eine Zeile!

INTO MyTable INSERT (PROP1, ...) VALUES ( 'aaa', ...);

Wenn Ihr etwas löschen, die nicht existiert, wird nichts passieren.

INSERT INTO schema.myFoo ( primary_key        , value1          , value2         )
                         SELECT 'bar1' AS primary_key ,'baz1' AS value1 ,'bat1' AS value2 FROM DUAL WHERE (SELECT 1 AS value FROM schema.myFoo WHERE LOWER(primary_key) ='bar1' AND ROWNUM=1) is null;

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top