Oracle Insert via Select 1 つのテーブルに行がない場合がある複数のテーブルから
質問
コードと長い ID を持つ説明を含むコード値テーブルが多数あります。
ここで、いくつかのコードを参照するアカウント タイプのエントリを作成したいので、次のようなものになります。
insert into account_type_standard (account_type_Standard_id,
tax_status_id, recipient_id)
( select account_type_standard_seq.nextval,
ts.tax_status_id, r.recipient_id
from tax_status ts, recipient r
where ts.tax_status_code = ?
and r.recipient_code = ?)
これにより、それぞれのコードに一致するものが見つかった場合、tax_status テーブルと受信者テーブルから適切な値が取得されます。残念ながら、recipient_code は null 可能であるため、?置換値は null になる可能性があります。もちろん、暗黙的結合は行を返さないため、テーブルに行は挿入されません。
?でNVLを使用してみました。そして r.recipient_id についてです。
r.recipient_code = ? で外部結合を強制しようとしました。(+) を追加しますが、これは明示的な結合ではないため、Oracle はまだ行を追加しませんでした。
これを行う方法を知っている人はいますか?
明らかに、このステートメントを変更して、recipient_id の検索を外部で実行し、? を付けることができます。r.recipient_id の代わりに、受信者テーブルからはまったく選択しませんが、これをすべて 1 つの SQL ステートメントで実行したいと考えています。
解決
この場合、外部結合は「期待どおり」に機能しません。これは、テーブルの条件が一致する場合にのみデータが必要であることを Oracle に明示的に伝えているためです。そのシナリオでは、外部結合は役に立たなくなります。
回避策
INSERT INTO account_type_standard
(account_type_Standard_id, tax_status_id, recipient_id)
VALUES(
(SELECT account_type_standard_seq.nextval FROM DUAL),
(SELECT tax_status_id FROM tax_status WHERE tax_status_code = ?),
(SELECT recipient_id FROM recipient WHERE recipient_code = ?)
)
編集]サブセレクトから複数の行が期待される場合は、句にrownum = 1を追加するか、maxやminなどの集合体を使用することができます。もちろん、これがすべての場合に最適な解決策であるとは限りません。
[編集] コメントごとに、
(SELECT account_type_standard_seq.nextval FROM DUAL),
ちょうどよい
account_type_standard_seq.nextval,
他のヒント
Oglester のソリューションを少し簡略化したバージョン (シーケンスは DUAL からの選択を必要としません:
INSERT INTO account_type_standard
(account_type_Standard_id, tax_status_id, recipient_id)
VALUES(
account_type_standard_seq.nextval,
(SELECT tax_status_id FROM tax_status WHERE tax_status_code = ?),
(SELECT recipient_id FROM recipient WHERE recipient_code = ?)
)
質問では、 ts.tax_status_code が主キーなのか代替キーなのかがわかりませんでした。受信者コードについても同じです。これは知っておくと便利です。
バインド変数が null になる可能性には、次のように OR を使用して対処できます。同じものを最初の 2 つのバインド変数にバインドします。
パフォーマンスが心配な場合は、バインドする値が null かどうかを確認し、OR を避けるために別の SQL ステートメントを発行することをお勧めします。
insert into account_type_standard
(account_type_Standard_id, tax_status_id, recipient_id)
(
select
account_type_standard_seq.nextval,
ts.tax_status_id,
r.recipient_id
from tax_status ts, recipient r
where (ts.tax_status_code = ? OR (ts.tax_status_code IS NULL and ? IS NULL))
and (r.recipient_code = ? OR (r.recipient_code IS NULL and ? IS NULL))
試す:
insert into account_type_standard (account_type_Standard_id, tax_status_id, recipient_id)
select account_type_standard_seq.nextval,
ts.tax_status_id,
( select r.recipient_id
from recipient r
where r.recipient_code = ?
)
from tax_status ts
where ts.tax_status_code = ?
insert into account_type_standard (account_type_Standard_id, tax_status_id, recipient_id)
select account_type_standard_seq.nextval,
ts.tax_status_id,
( select r.recipient_id
from recipient r
where r.recipient_code = ?
)
from tax_status ts
where ts.tax_status_code = ?
insert into received_messages(id, content, status)
values (RECEIVED_MESSAGES_SEQ.NEXT_VAL, empty_blob(), '');