PostgreSQL宣言的テーブルパーティショニング構築とプルーニング確認

PostgreSQL Database Performance SQL
結論

PARTITION BY RANGE で親を作成し、検索条件ではパーティションキーに関数を適用せず生の範囲指定を行います。

-- PostgreSQL公式仕様:月別宣言的パーティショニングの作成
-- 1. 親テーブル作成
CREATE TABLE logs (
  id bigserial,
  log_level text,
  created_at timestamptz not null,
  primary key (id, created_at)
) PARTITION BY RANGE (created_at);

-- 2. 2026年7月用の子テーブル作成
CREATE TABLE logs_y2026m07 PARTITION OF logs
  FOR VALUES FROM ('2026-07-01 00:00:00+00') TO ('2026-08-01 00:00:00+00');

-- 3. 範囲外データのクラッシュを防ぐ DEFAULT パーティション
CREATE TABLE logs_default PARTITION OF logs DEFAULT;

PostgreSQL公式仕様:Partition Pruning(間引き)の仕組み

PostgreSQL公式ドキュメント(postgresql.org/docs/current/ddl-partitioning.html)の規定通り、宣言的パーティショニングを設定したテーブルに対して検索クエリを発行すると、オプティマイザは Partition Pruning (パーティション間引き) 機能を起動します。

のような検索条件がある場合、2026年7月以外の全子テーブルの検索を計画段階および実行段階で完全削除(Skip)し、極小の対象子テーブルのみを高速スキャンします。


実際に起こる障害:WHERE 句の関数適用による全子テーブルフルスキャン

Pruning 機能を無効化させてしまう開発上の落とし穴が、WHERE 句でパーティションキー(created_at)を関数で包んでしまうパターン です。

-- ❌ 事故:Pruning が無効化され、過去数年分の全子テーブルを走査するダメなクエリ
SELECT * FROM logs 
WHERE date_trunc('month', created_at) = '2026-07-01'::timestamptz;

-- ⭕ 正解:生のカラムに対して範囲演算子を指定する(Pruning が正常動作)
SELECT * FROM logs 
WHERE created_at >= '2026-07-01 00:00:00+00' 
  AND created_at < '2026-08-01 00:00:00+00';

関数を適用すると、オプティマイザが範囲境界の計算を行えず、数十〜数百個存在する全パーティション子テーブルに対して全件スキャンが走り、大規模障害を引き起こします。


構築手順

  1. 親テーブルを PARTITION BY RANGE (created_at) で作成し、主キーには必ずパーティションキーを含める
  2. 月別または年別の子テーブルを FOR VALUES FROM (...) TO (...) で定義する
  3. 定義範囲外データによる ERROR: no partition of relation クラッシュを防ぐため DEFAULT パーティションを併設する
  4. EXPLAINSubplans Removed が表示され、目的の子テーブルのみが走査されているか検証する