やあ、お疲れ様。PostgreSQLの設計や運用で頭を悩ませることは多いけど、今日はその中でも「シーケンス(Sequence)」について少し深掘りしてみようか。
PostgreSQLを触り始めると、誰もが最初に `SERIAL` 型や `GENERATED ALWAYS AS IDENTITY` に出会うよね。「主キーを自動で採番してくれる便利なやつ」という認識で使い始めるはずだ。
でも、実務で大規模なデータ移行をしたり、一括でIDを付与した後にデータを投入したりする際、このシーケンスの「裏側」を直接叩かなきゃいけない場面が必ずやってくる。今日は、現場でよく使うシーケンス操作関数の「使いどころ」と「注意点」について、少しだけ実戦的な話をさせてくれ。
—
シーケンス操作の「基本三銃士」
PostgreSQLには、シーケンスを直接操作するための関数がいくつか用意されている。まず、最低限押さえておくべきなのはこの3つだ。
1. `nextval(‘シーケンス名’)`
これは説明不要かもしれないね。シーケンスの現在値をインクリメントして、その「新しい値」を返す関数だ。INSERT文で `DEFAULT` を使う代わりに、明示的に値を突っ込みたい時によく使う。
— 次のIDを確保して、それを変数に格納するようなイメージ
SELECT nextval(‘users_id_seq’);
2. `currval(‘シーケンス名’)`
「最後に自分が使った値は何だっけ?」を確認したい時に使う。ただし、注意してほしい。これは同じセッション(接続)内で、直前に `nextval` を実行していないとエラーになるんだ。他の人が別の場所でシーケンスを進めていても、自分のセッションで実行した最新の値だけを返してくれる。安全だよね。
3. `setval(‘シーケンス名’, 値, [is_called])`
これが一番トラブルシューティングで使うやつだ。例えば、CSVから大量のデータをインポートした後に、シーケンスの現在値がズレてしまって「主キー重複エラー」が起きる…なんて経験はないかな? そんな時にシーケンスの現在値を強制的に上書きする。
— シーケンスを1000番にセットする(次は1001から始まる)
SELECT setval(‘users_id_seq’, 1000);
—
現場で遭遇する「落とし穴」
ここで少しだけ、先輩からのアドバイスだ。教科書通りに使うだけなら簡単なんだけど、現場には罠がある。
注意点1:トランザクションとシーケンスは別物
ここが一番ハマりやすいポイントだ。「トランザクションをロールバックすれば、シーケンスのカウントも元に戻るよね?」と思っているなら要注意。
シーケンスは、トランザクションのロールバックの影響を受けない。
もし `nextval` を実行した後にそのトランザクションを `ROLLBACK` しても、シーケンスの値は進んだままなんだ。だから、シーケンスのIDに「欠番がないこと」を期待するような設計は絶対に避けるべきだね。IDはあくまで「ユニークであること」を保証するためのものだと割り切ろう。
注意点2:`setval` の第3引数
`setval` には第3引数(boolean)がある。ここを誤解しているエンジニアが結構多いんだ。
- `setval(‘seq’, 100, true)` : 指定した値(100)が既に「使用済み」とみなされ、次は101になる。
- `setval(‘seq’, 100, false)` : 指定した値(100)は「まだ使われていない」とみなされ、次は100になる。
基本的には `true` で使うことがほとんどだけど、インポートの都合で「次はきっちりこの数字から始めたい」という時は、この引数が命綱になる。
—
まとめ:結局、どう付き合うべき?
シーケンス操作関数は、いわばデータベースの「心臓部」を直接触るようなものだ。日常的なアプリケーション開発ではあまり意識する必要はないけれど、運用や保守のフェーズではこれを知っているだけで解決できるトラブルの幅がグッと広がる。
もし君が今、データベースの移行やデータパッチを当てようとしているなら、まずは `currval` で現在値を確認し、必要なら `setval` で整合性を保つ。この手順を叩き込んでおけば、怖いものはないよ。
何か困ったことがあったら、いつでも聞いてくれ。現場からは以上だ。また次のトピックで会おう。
コメント