【実務・中級編】 PL/pgSQLのカーソル – PostgreSQL

PostgreSQLのカーソル、ちゃんと使えてる?メモリを爆発させないための「一行ずつ」の作法

やあ。データベースを触っていると、たまにこういう場面に出くわさないか?

「数百万件あるテーブルを全件スキャンして、複雑なロジックで計算してから別のテーブルに書き込みたい」

そんなとき、安易に `SELECT FROM big_table` を実行して、アプリケーション側で全部メモリに載せようとすると……まあ、大抵の場合はメモリ不足でプロセスが死ぬか、サーバーが悲鳴を上げるのがオチだよね。

PostgreSQLのストアドプロシージャ(PL/pgSQL)を書くとき、この「巨大な結果セットをどう捌くか」という課題に対する強力な武器が、そう、「カーソル」だ。今日は、実務で明日から使えるカーソルの扱い方について、ちょっとしたコツを交えて話していこうと思う。

—

なぜ「カーソル」が必要なのか?

PL/pgSQLで `FOR record IN SELECT …` という書き方を見たことがあるかもしれない。あれは便利だけど、実は裏側で結果セット全体を一旦メモリにロードしようとする性質がある。件数が少ないうちはいいけれど、数百万行レベルになると、PostgreSQLのバックエンドプロセスのメモリを圧迫して、パフォーマンスがガタ落ちになるんだ。

カーソルは、「結果セットへのポインタを保持し、必要な分だけをメモリに引き出す」という仕組みだ。これを使えば、どれだけ巨大なデータセット相手でも、メモリ消費量を一定に保ったまま処理を完結できる。

—

カーソルの基本サイクル:4つのステップ

カーソルの扱いは、料理の準備と片付けに似ている。この4つのステップを体に叩き込んでおこう。

1. DECLARE: カーソルに名前とクエリを紐付ける。
2. OPEN: クエリを実行し、ポインタをセットする。
3. FETCH: 一行ずつ(あるいは数行ずつ)データを取り出す。
4. CLOSE: 使い終わったら必ず閉じる。

実践コード:こんな風に書く

DO $$
DECLARE
— 1. カーソルの宣言
cur_users CURSOR FOR SELECT id, username FROM users WHERE is_active = true;
rec RECORD;
BEGIN
— 2. カーソルのオープン
OPEN cur_users;

LOOP
— 3. FETCHで一行ずつ取り出す
FETCH cur_users INTO rec;

— データがなくなったらループを抜ける
EXIT WHEN NOT FOUND;

— ここで好きな処理をする
RAISE NOTICE ‘処理中: %’, rec.username;
END LOOP;

— 4. クローズ
CLOSE cur_users;
END $$;

—

実務で「おっ、分かってるね」と思われるポイント

教科書通りならこれで終わりなんだけど、現場で使うならもう少しだけ踏み込んでおこう。

1. FORループの「カーソル版」を使う

実は、わざわざ `OPEN` や `CLOSE` を手書きしなくても、PostgreSQLは便利な構文を用意してくれているんだ。

FOR rec IN cur_users LOOP
— これならOPEN/CLOSEを自動でやってくれる!
— しかもコードが圧倒的に読みやすい。
RAISE NOTICE ‘ユーザーID: %’, rec.id;
END LOOP;

基本的にはこの書き方で十分だ。ただし、カーソルの動的な制御(パラメータを渡したい、特定の条件下でスキップしたいなど)が必要なときは、明示的な `OPEN` を使うのがベターだね。

2. トランザクションを意識する

カーソルは基本的にトランザクション内でのみ有効だ。もし `COMMIT` を発行すると、その瞬間にカーソルは無効化される。だから、数百万件を処理するためにカーソルを使う場合は、ループの中で小まめにコミットするような実装はできない(というか、してはいけない)。
「大きな処理をどう小分けにするか」という設計の話になると、また別の機会が必要だけど、まずは「カーソルは一つのトランザクションと運命共同体」と覚えておいてくれ。

—

最後に:エンジニアとしての心得

「ループ処理」というのは、SQLの集合演算(`UPDATE … WHERE …` のような一括処理)に比べれば、どうしても低速になりがちだ。

だから、僕が後輩にいつも言っているのはこうだ。
「まずは集合演算で一気に片付けられないか、3分だけ考えてみてくれ。それでも無理なら、カーソルを使おう」

カーソルは便利だけど、多用すればいいってもんじゃない。適材適所。その判断ができるのが、いいデータベースエンジニアだと思うよ。

さて、今日の解説はここまで。もし「もっと複雑な条件でカーソルを回したい」「パフォーマンスがどうしても出ない」なんて悩みがあれば、またいつでも聞いてくれ。現場からは以上だ!

コメント

タイトルとURLをコピーしました