多数の行を挿入するOracleクエリを実行できる必要がありますが、主キーが存在するかどうかを確認し、存在する場合はその挿入をスキップします。何かのようなもの:
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;
これはOracleで可能ですか?
PostgreSQLまたはMySQLでこれを行う方法を教えていただければ、ボーナスポイントになります。
このステートメントはMERGEと呼ばれます。見上げてください、私は怠け者です。
ただし、MERGEはアトミックではないことに注意してください。これにより、次の効果が生じる可能性があります(ありがとう、マリウス)。
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
_
パーティーに遅れるが...
Oracle 11.2.0.1には、これを行うことができるセマンティックヒントがあります。IGNORE_ROW_ON_DUPKEY_INDEX
例:
insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(customer_orders,pk_customer_orders) */
into customer_orders
(order_id, customer, product)
values ( 1234, 9876, 'K598')
;
[〜#〜] update [〜#〜]:このヒントは機能しますが(正しくつづった場合)、 より良いアプローチ Oracle 11R2を必要としません:
最初のアプローチ-上記のセマンティックヒントの直接翻訳:
begin
insert into customer_orders
(order_id, customer, product)
values ( 1234, 9876, 'K698')
;
commit;
exception
when DUP_VAL_ON_INDEX
then ROLLBACK;
end;
2番目のアプローチ-多くの競合がある場合、上記の両方のヒントよりも速いlot:
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;
これは、挿入するアイテムがまだ存在しない場合にのみ挿入します。
以下と同じ動作:
if not exists (...) insert ...
t-SQLで
insert into destination (DESTINATIONABBREV)
select 'xyz' from dual
left outer join destination d on d.destinationabbrev = 'xyz'
where d.destinationid is null;
きれいではないかもしれませんが、便利です:)
DUALとNOT EXISTSを組み合わせて、要件をアーカイブできます。
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'
);
他のテーブルからマージするのではなく、新しいデータを挿入する場合は...これを思いつきました。おそらくこれを行うためのより良い方法はありますか?
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);
そのコードがクライアント上にある場合、それを排除するためにサーバーに何度もアクセスします。
MyFooと同じ構造を持つすべてのデータをTと言うTempportaryテーブルに挿入します
それから
insert myFoo
select *
from t
where t.primary_key not in ( select primary_key from myFoo)
これは他のデータベースでも動作するはずです-私はSybaseでこれを行いました
回線を介してすべてのデータをコピーしたため、挿入する新しいデータが非常に少ない場合は最適ではありません。
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;
上記のコードを使用しました。それは長いですが、シンプルで私のために働いた。 Michealのコードに似ています。
これは、erikkallenが投稿したコメントに対する回答です。
一時テーブルは必要ありません。数行しかない場合は、(SELECT 1 FROM dual UNION SELECT 2 FROM dual)でできます。なぜあなたの例はORA-0001を与えるのでしょうか?マージはインデックスキーの更新ロックを取得し、Sess1がコミットまたはロールバックするまで続行しませんか? –エリッカレン
さて、自分で試して、同じエラーが発生するかどうかを教えてください:
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
_
テーブルが他のテーブルから「独立」している場合(つまり、カスケード削除をトリガーしないか、外部キー関係をnullに設定しない場合)、最初に行を削除してから再度挿入するのが良い方法です。次のようになります。
MyTable WHEREからの削除prop1 = 'aaa'; //最大で1行を選択すると仮定します!
INSERT INTO MyTable(prop1、...)VALUES( 'aaa'、...);
存在しないものを削除する場合、何も起こりません。
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;