今日、PL/SQLのスニペットを書きました。
declare
first_id number;
second_id number;
begin
insert into table (sort_nr, text_id, unit_id) values(...,
table_seq.nextval, table_seq.nextval) returning text_id, unit_id
into first_id, second_id;
dbms_output.put_line(first_id);
dbms_output.put_line(second_id);
end;
そして、独特の拘束違反を受けました。さらに調査した結果、first_id
およびsecond_id
には同じ値が含まれていました。
私の質問は次のとおりです。同じステートメントで同じシーケンスを複数回呼び出して後続の番号を受け取る制限はありますか?私の観点からは、nextval
はクエリのスコープ内で1回だけ呼び出されてキャッシュされるようです。
余談ですが、2つの列で同じシーケンスを使用することを避けるために、そのくだらないスキーマを変更することはできません。
これはnextval
の予想される動作です 文書化 :
NEXTVALへの参照を含む単一のSQLステートメント内で、Oracleはシーケンスを1回インクリメントします。
- SELECTステートメントの外部クエリブロックによって返される各行
[...]これらのいずれかの場所にNEXTVALへの参照が複数含まれている場合、Oracleはシーケンスを1つインクリメントし、すべてのNEXTVALに対して同じ値を返します。
つまり、プレーンSQLを使用してこの制限を克服することはできません。PL/ SQLが必要になります。両方のフィールドに入力するトリガー、またはシーケンス呼び出しをラップする関数のいずれか。 11gを使用したこのような関数の例を次に示します。
SQL> create sequence table_seq;
Sequence created.
SQL> create table test (sort_nr number, text_id number, unit_id number);
Table created.
SQL> create or replace function getid return number is begin return table_seq.nextval; end;
2 /
Function created.
SQL> insert into test values (1, getid, getid);
1 row created.
SQL> select * from test;
SORT_NR TEXT_ID UNIT_ID
---------- ---------- ----------
1 1 2
同じステートメントで同じシーケンスを複数回使用することに制限はありません。ただし、単一行のすべての呼び出しは同じ値を返します。
SQL> ed
Wrote file afiedt.buf
1 select foo_seq.nextval, foo_seq.nextval, foo_seq.nextval
2 from dual
3* connect by level <= 5
SQL> /
NEXTVAL NEXTVAL NEXTVAL
---------- ---------- ----------
22 22 22
23 23 23
24 24 24
25 25 25
26 26 26
text_id
とunit_id
の両方を生成しようとしている場合、通常、2つのシーケンス、つまりtest_id_seq
とunit_id_seq
の両方を参照する方が理にかなっています。 INSERT
ステートメント。 first_id
を個別に呼び出してsecond_id
およびtable_seq.nextval
変数を使用することにより、INSERT
ステートメントを実行する前にfirst_id
およびsecond_id
を入力することもできます。シーケンスを参照するのではなくINSERT
ステートメントで使用します。
私はジャスティンケイブの提案に従い、別のシーケンスを使用しますが、本当に同じシーケンスを使用する必要がある場合は、INCREMENT値を2に変更して、次のように挿入に1を追加するだけです。
DROP SEQUENCE S1;
DROP TABLE T1;
CREATE SEQUENCE S1 INCREMENT BY 2 START WITH 1;
CREATE TABLE T1 (C1 Number(10), C2 Number(10));
INSERT INTO T1 VALUES (S1.nextval, S1.nextval + 1);
INSERT INTO T1 VALUES (S1.nextval, S1.nextval + 1);
INSERT INTO T1 VALUES (S1.nextval, S1.nextval + 1);
COMMIT;
SELECT * FROM T1;
独自の制限がある別のオプションは、2番目のエントリに負のシーケンス値を使用することです。