Oracleシーケンスの現在値を一気に何番もカウントアップする方法のメモ。 個人的に、基本 INCREMENT BY が「1」の(=1ずつ採番していく)シーケンスをよく使うので、たまにこういうのが必要になるのである。

SELECT TEST_SEQ.NEXTVAL FROM DUAL を呼び出すたびにシーケンス値が1ずつカウントアップされていくため、「10まで進めたい」と思ったら sqlplus とかで DB に接続した後 SELECT TEST_SEQ.NEXTVAL FROM DUAL を10回投げればいいだけである。 ただこれが**「100,000(10万)まで進めたい」**だとそうはいかない。 そんなときに「一気に10万まで数字を進める」やり方である。


手順:INCREMENT BY を一時的に変える

一番簡単なのは、

INCREMENT BY 値を一時的にすげーでかい値に変える ⇒ 一回だけ NEXTVAL を呼び出す ⇒ 元に戻す

例えば以下のような INCREMENT BY が1のシーケンス TEST_SEQ があったとする。

create sequence TEST_SEQ
   increment by 1
   start with 1
   maxvalue 99999999
   minvalue 0
   NOCYCLE
   ORDER
   NOCACHE

作った直後はまだ「1」から採番してる状況である。 このシーケンスの現在値を「100,000(10万)番まで一気にカウントあげたい」という場合、

alter sequence TEST_SEQ increment by 100000;     -- ①
select TEST_SEQ.nextval from dual;               -- ②
alter sequence TEST_SEQ increment by 1;          -- ③

とすれば次の NEXTVAL が一気に10万1番になる。

もともと1ずつ採番するシーケンスを①で一時的に10万単位で採番するように変更し、10万単位採番の状態で1回採番して(②)一気に10万分番号を飛ばして、また1ずつ採番に戻す(③)。


そもそもなぜこんなことをするのか

「こんなことするくらいなら最初から10万単位採番でシーケンス作ればいいじゃん」 と思うかもしれないが、取得したシーケンスをテーブルの KEY にしてるようなケースで、無理矢理テストデータ突っ込んだりとかしてテーブルだけが先に進んでしまい、シーケンスは置いてけぼりにしてしまった場合等で、上記のような対処が必要になるときがある。 (というか実際、あったのである)

例えば、Oracleシーケンスの値を主 KEY に持つテーブルがあって、プログラム側ではそのテーブルに毎回 SELECT TEST_SEQ.NEXTVAL FROM DUAL で返ってきた値を主 KEY として採用しているとする。 そのとき、プログラムとは別の口から Oracleシーケンスを使わずに無理矢理データを作ると、その後プログラムを呼び出したときに、採番される Oracleシーケンス値がテーブルに既存の No とぶつかって一意制約になってしまう。 以下のようなケースである。↓

作成できるか?★No(主KEY)NAME作成契機このデータ作成直後にNEXTVALを呼び出すと
1test1プログラム2
2test2プログラム3
3testX3テストデータ手作成3
×3test3プログラム4

のように、No.3 を Oracleシーケンスを使わずに手で無理やりつくったので、テーブルの最大 No とシーケンスの現在値がずれて、次にプログラムからレコード作ろうとしたときに一意制約違反になって落ちてしまう。 こういうとき、TEST_SEQ を3まで進めて採番値が被らないよう細工する必要があり、↑に書いたような対処をする。

ただまあこの程度(せいぜい10くらい数字が乖離している程度)なら、SELECT TEST_SEQ.NEXTVAL FROM DUAL を乖離してる数分、投げる方が手っ取り早い。 (このケースだとわずか1しかズレていないから、1回 SELECT TEST_SEQ.NEXTVAL FROM DUAL を投げるだけでいい) 数字のズレ方によって、使う手段を切り分けるべきであろう。


愚直にループで回すやり方

ちなみに愚直(?)にやるやり方として以下のようなのもある。

declare
   nmb_test number(10) := 0;
begin
   for i in 1..100000 loop
       SELECT TEST_SEQ.NEXTVAL INTO nmb_test FROM DUAL;
   end loop;

   DBMS_OUTPUT.PUT_LINE('SEQUENCE_NO='||trim(to_char(nmb_test)));
end;
/

要するに10万回 SELECT TEST_SEQ.NEXTVAL FROM DUAL を投げるだけのもんであるが、まじで10万回ループするのでカウントアップしたい数字が遠いほど時間がかかるし、そこまでおすすめできるものではない。。