SEQUENCE についての FAQ
シーケンスの現在値 NEXTVAL と CURRVAL
インスタンス単位でのシーケンスの現在値を知ることはできない(※)。わかるのは自らが採番した後の現在値(=次の値)だけとなる。
(※) CURRVAL という擬似列で現セッション中で最後に採番した番号(=現在値と言い換えることもできる)を知ることはできる。そのセッションで採番 (NEXTVAL) を行なっていない場合には、CURRVAL 擬似列を参照することはできない。
ORA-08002: 順序 sequence_name.CURRVALはこのセッションではまだ定義されていません
のエラーが発生する。
シーケンスの欠番 ケース1
シーケンスは採番のたびにディクショナリにアクセスせずにメモリ上にバッファされている番号を戻す。
これはメモリ上に次の値と次回のディクショナリの更新値を保持しているのではないかと思う。
ディクショナリの値はシングルインスタンスに限れば、
前回のディクショナリの値 <= 現在値 < 現在のディクショナリの値
(現在のディクショナリの値 = 前回のディクショナリの値 + キャッシュサイズ)
となる。
このディクショナリの値は正常停止(シャットダウン)しても値が巻き戻ることはない。
そのため次回起動時にキャッシュ上に確保されていた番号分は欠番として再開されることになる。
以下の SQL でディクショナリの値は取得できる。
SELECT SEQUENCE_NAME, LAST_NUMBER FROM USER_SEQUENCES
シーケンスの欠番 ケース2
シーケンスは揮発性の共有プール(メモリ)に格納されている。そのため長期間使用されずに放置されたシーケンスの使用領域に期限切れが発生して共有プールから追い出されることがある。このときのメモリ上に確保された部分は消去される。
この原因の欠番発生を防ぐには DBMS_SHARED_POOL パッケージの KEEP プロシージャを使用する。
シーケンス名を指定し KEEP を呼び出すことで一度ローディングされたシーケンスは期限切れによるエージアウト(エージング・アウト)が発生しないようになる。
⇒ DBMS_SHARED_POOL パッケージの使用例
シーケンスの欠番 ケース3
上述のとおり、シーケンスはキャッシュをメモリ上に確保している。
稼動中にシーケンスのキャッシュが保存されている ディクショナリ・キャッシュ (SHARED_POOL) メモリをクリアする操作を行なうと
メモリ上に確保された部分が消去される。つまりキャッシュに確保されたが使用しなかった番号はすべて欠番となる。
キャッシュを消してしまう命令の例
ALTER SYSTEM FLUSH SHARED_POOL ;
-- ※補足:ALTER SYSTEM FLUSH BUFFER_CACHE (10g) では欠番にならない
この2つの命令は結構インパクトが大きい。仕切り直しのつもりで安易に本番環境のバッチ処理(の前後)などに直接組み込んでしまうのはお勧めできない。使う場合でも使いどころを間違わないようにしたい。
シーケンスの欠番 ケース4
通常、シーケンスは決して逆戻りはしない(※)。トランザクションに含まれていても同様である。
これはトランザクションが ROLLBACK することで欠番が発生することをあらわす。
(※) リカバリ、シーケンスの再作成やインポートなどの場合を除く
シーケンスの欠番 ケース5
遅延セグメント作成 とシーケンス
セグメント実体化されるときの INSERT 文中でシーケンスを使った場合にそこで取得された番号は欠番となる。
遅延セグメント作成(deferred segment creation) は Oracle 11g R2 の新機能であるがデフォルトで ON なので、旧来の初期化パラメータ設定と CREATE TABLER 文で環境を作成していると非常に高い確率で最初の番号は欠番になっていると思われる。
リリース直前できたてほやほや環境の検収テストで指摘されるかもしれないので頭の片隅においておこう。
ちなみに SYS スキーマ ではセグメントの作成遅延は起きないようである。
注意※ この遅延作成機能は EE : エンタープライズ版の機能なので SE One や SE では発生しない。(Oracle 11g R2 時点)
SQL> create table hoge1 (id number);
表が作成されました。
SQL> create table hoge2 (id number);
表が作成されました。
SQL> create sequence seq_hoge;
順序が作成されました。
SQL>
SQL> insert into hoge1 values (seq_hoge.nextval);
1行が作成されました。
SQL> insert into hoge1 values (seq_hoge.nextval);
1行が作成されました。
SQL> insert into hoge2 values (seq_hoge.nextval);
1行が作成されました。
SQL> insert into hoge2 values (seq_hoge.nextval);
1行が作成されました。
SQL> select * from hoge1;
ID
----------
2
3
SQL> select * from hoge2;
ID
----------
5
6
以上のように 1 と 4 がスキップされてしまう。
セグメントを実体化したあとに同じ SQL が再度評価されているのか?
RAC とシーケンスの飛び番
シーケンス・ジェネレータはインスタンス毎に存在する。すなわちキャッシュもインスタンス毎に用意される。
デフォルトのシーケンスの場合、キャッシュサイズ 20、NOORDER であるので、たとえば
インスタンスAのキャッシュ 1 〜 20
インスタンスBのキャッシュ 21 〜 40
ディクショナリの値 41
のようにキャッシュされる。このため、NOORDER の場合に 1, 21, 2, 22 ... のように採番されることがある。
これは ORDER にして回避することができる。
しかしキャッシュはされなくなる。シングルインスタンスでなければ ORDER とキャッシュは両立しない。
ある特定のインスタンスから取得すれば両立するが、そのインスタンスが停止するとすべての採番が停止する。
シーケンスと PL/SQL
シーケンスオブジェクトは SQL からしかアクセスできない。そのため PL/SQL からシーケンスにアクセスするには SQL を経由する。〜 Oracle 10g
Oracle 11g からは PL/SQL で直接シーケンスオブジェクトにアクセスできる。
シーケンスの制限事項
シーケンスの最大値は 28 桁(10^28 -1)、負の場合は 27桁(10^27 -1)
ちなみに 0〜10^28-1 を増分1で使い切ろうとすると、毎秒 1000 万回採番して約 3 兆年かかるので、おうばん振る舞い*1 しても大丈夫である。