einfügen, wenn nicht Orakel existiert
-
19-09-2019 - |
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.
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;