web-dev-qa-db-ja.com

PL / SQLステートメントで同じシーケンスを2回使用する

今日、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つの列で同じシーケンスを使用することを避けるために、そのくだらないスキーマを変更することはできません。

6
Michael-O

これは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
7
Vincent Malgrat

同じステートメントで同じシーケンスを複数回使用することに制限はありません。ただし、単一行のすべての呼び出しは同じ値を返します。

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_idunit_idの両方を生成しようとしている場合、通常、2つのシーケンス、つまりtest_id_sequnit_id_seqの両方を参照する方が理にかなっています。 INSERTステートメント。 first_idを個別に呼び出してsecond_idおよびtable_seq.nextval変数を使用することにより、INSERTステートメントを実行する前にfirst_idおよびsecond_idを入力することもできます。シーケンスを参照するのではなくINSERTステートメントで使用します。

3
Justin Cave

私はジャスティンケイブの提案に従い、別のシーケンスを使用しますが、本当に同じシーケンスを使用する必要がある場合は、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番目のエントリに負のシーケンス値を使用することです。

1
Leigh Riffel