PostgreSQL チートシート:開発者向けクイックリファレンス
PostgreSQL の簡易参照資料
目次
日常的な PostgreSQL の作業(コネクション、SQL 構文、psql メタコマンド、パフォーマンス、JSON、ウィンドウ関数など)のための速習リファレンスです。
PostgreSQL の詳細に入る前に、簡潔なクロスデータベースのリファresher をお望みであれば、最も有用な SQL コマンドをまとめた SQL チートシート が便利な相棒になります。

コネクションと基本
# コネクション確立
psql -h HOST -p 5432 -U USER -d DB
psql $DATABASE_URL
# psql 内部でのコマンド
\conninfo -- 接続情報表示
\l[+] -- データベース一覧
\c DB -- DB への接続
\dt[+] [schema.]pat -- テーブル一覧
\dv[+] -- ビュー一覧
\ds[+] -- シーケンス一覧
\df[+] [pat] -- 関数一覧
\d[S+] name -- テーブル/ビュー/シーケンスの詳細
\dn[+] -- スキーマ一覧
\du[+] -- ロール一覧
\timing -- クエリ実行時間表示の切り替え
\x -- 拡張表示
\e -- $EDITOR でバッファを編集
\i file.sql -- ファイル実行
\copy ... -- クライアント側 COPY
\! shell_cmd -- シェルコマンド実行
データ型(一般的なもの)
- 数値:
smallint,integer,bigint,decimal(p,s),numeric,real,double precision,serial,bigserial - テキスト:
text,varchar(n),char(n) - 真偽値:
boolean - 時間日付:
timestamp [with/without time zone],date,time,interval - UUID:
uuid - JSON:
json,jsonb(推奨) - 配列:
type[]例:text[] - ネットワーク:
inet,cidr,macaddr - 幾何学:
point,line,polygonなど
PostgreSQL バージョンの確認
SELECT version();
PostgreSQL サーバーバージョン:
pg_config --version
PostgreSQL クライアントバージョン:
psql --version
DDL (作成 / 変更)
-- スキーマとテーブルの作成
CREATE SCHEMA IF NOT EXISTS app;
CREATE TABLE app.users (
id bigserial PRIMARY KEY,
email text NOT NULL UNIQUE,
name text,
active boolean NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now(),
profile jsonb,
tags text[]
);
-- 変更
ALTER TABLE app.users ADD COLUMN last_login timestamptz;
ALTER TABLE app.users ALTER COLUMN name SET NOT NULL;
ALTER TABLE app.users DROP COLUMN tags;
-- 制約
ALTER TABLE app.users ADD CONSTRAINT email_lower_uk UNIQUE (lower(email));
-- インデックス
CREATE INDEX ON app.users (email);
CREATE UNIQUE INDEX CONCURRENTLY users_email_uidx ON app.users (lower(email));
CREATE INDEX users_profile_gin ON app.users USING gin (profile);
CREATE INDEX users_created_at_idx ON app.users (created_at DESC);
DML (挿入 / 更新 / Upsert / 削除)
INSERT INTO app.users (email, name) VALUES
('a@x.com','A'), ('b@x.com','B')
RETURNING id;
-- Upsert (ON CONFLICT)
INSERT INTO app.users (email, name)
VALUES ('a@x.com','Alice')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name, updated_at = now();
UPDATE app.users SET active = false WHERE last_login < now() - interval '1 year';
DELETE FROM app.users WHERE active = false AND last_login IS NULL;
クエリの基本
SELECT * FROM app.users ORDER BY created_at DESC LIMIT 20 OFFSET 40; -- ページネーション
-- フィルタリング
SELECT * FROM app.users WHERE email ILIKE '%@example.%' AND active;
-- 集計と GROUP BY
SELECT active, count(*) AS n
FROM app.users
GROUP BY active
HAVING count(*) > 10;
-- 結合 (Joins)
SELECT o.id, u.email, o.total
FROM app.orders o
JOIN app.users u ON u.id = o.user_id
LEFT JOIN app.discounts d ON d.id = o.discount_id;
-- DISTINCT ON (Postgres 固有機能)
SELECT DISTINCT ON (user_id) user_id, status, created_at
FROM app.events
ORDER BY user_id, created_at DESC;
-- CTE (共通テーブル式)
WITH recent AS (
SELECT * FROM app.orders WHERE created_at > now() - interval '30 days'
)
SELECT count(*) FROM recent;
-- 再帰 CTE
WITH RECURSIVE t(n) AS (
SELECT 1
UNION ALL
SELECT n+1 FROM t WHERE n < 10
)
SELECT sum(n) FROM t;
ウィンドウ関数
SELECT
user_id,
created_at,
sum(total) OVER (PARTITION BY user_id ORDER BY created_at) AS running_total,
row_number() OVER (PARTITION BY user_id ORDER BY created_at) AS rn,
lag(total, 1) OVER (PARTITION BY user_id ORDER BY created_at) AS prev_total
FROM app.orders;
JSON / JSONB
-- 抽出
SELECT profile->>'company' AS company FROM app.users;
SELECT profile->'address'->>'city' FROM app.users;
-- jsonb クエリ用インデックス
CREATE INDEX users_profile_company_gin ON app.users USING gin ((profile->>'company'));
-- 存在 / 包含
SELECT * FROM app.users WHERE profile ? 'company'; -- キーが存在する
SELECT * FROM app.users WHERE profile @> '{"role":"admin"}'; -- 包含する
-- jsonb の更新
UPDATE app.users
SET profile = jsonb_set(COALESCE(profile,'{}'::jsonb), '{prefs,theme}', '"dark"', true);
配列
-- 所属と包含
SELECT * FROM app.users WHERE 'vip' = ANY(tags);
SELECT * FROM app.users WHERE tags @> ARRAY['beta'];
-- 追加
UPDATE app.users SET tags = array_distinct(tags || ARRAY['vip']);
時間と日付
SELECT now() AT TIME ZONE 'Australia/Melbourne';
SELECT date_trunc('day', created_at) AS d, count(*)
FROM app.users GROUP BY d ORDER BY d;
-- インターバル
SELECT now() - interval '7 days';
トランザクションとロック
BEGIN;
UPDATE app.accounts SET balance = balance - 100 WHERE id = 1;
UPDATE app.accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- または ROLLBACK
-- イソレーションレベル
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ロックの確認
SELECT * FROM pg_locks l JOIN pg_stat_activity a USING (pid);
ロールと権限
-- ロール/ユーザーの作成
CREATE ROLE app_user LOGIN PASSWORD 'secret';
GRANT USAGE ON SCHEMA app TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
インポート / エクスポート
-- サーバー側 (スーパーユーザーまたは適切な権限が必要)
COPY app.users TO '/tmp/users.csv' CSV HEADER;
COPY app.users(email,name) FROM '/tmp/users.csv' CSV HEADER;
-- クライアント側 (psql)
\copy app.users TO 'users.csv' CSV HEADER
\copy app.users(email,name) FROM 'users.csv' CSV HEADER
パフォーマンスと可視化
EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT ...; -- 実際のランタイム
-- 統計ビュー
SELECT * FROM pg_stat_user_tables;
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 20; -- 拡張機能が必要
-- メンテナンス
VACUUM [FULL] [VERBOSE] table_name;
ANALYZE table_name;
REINDEX TABLE table_name;
Docker、Git、PostgreSQL を含む必須開発ツールの広範な概要については、開発者ツール:モダン開発ワークフローへの完全ガイド を参照してください。
必要に応じて拡張機能を有効化します:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS btree_gin;
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- 3 文字列検索
フルテキスト検索(クイック)
ALTER TABLE app.docs ADD COLUMN tsv tsvector;
UPDATE app.docs SET tsv = to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''));
CREATE INDEX docs_tsv_idx ON app.docs USING gin(tsv);
SELECT id FROM app.docs WHERE tsv @@ plainto_tsquery('english', 'quick brown fox');
ネイティブの Postgres 検索で十分か、別々の検索スタックを実行すべきかの判断に迷っている場合、PostgreSQL フルテキスト検索と Elasticsearch の比較 では、トレードオフを深く解説しています。
有用な psql 設定
\pset pager off -- ページャーを無効化
\pset null '∅'
\pset format aligned -- その他: unaligned, csv
\set ON_ERROR_STOP on
\timing on
便利なカタログクエリ
-- テーブルサイズ
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS size
FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC;
-- インデックス肥大化(概算)
SELECT schemaname, relname, indexrelname, idx_scan
FROM pg_stat_user_indexes ORDER BY idx_scan ASC NULLS FIRST LIMIT 20;
バックアップ / リストア
# ロジカルダンプ
pg_dump -h HOST -U USER -d DB -F c -f db.dump # カスタム形式
pg_restore -h HOST -U USER -d NEW_DB -j 4 db.dump
# 平文 SQL
pg_dump -h HOST -U USER -d DB > dump.sql
psql -h HOST -U USER -d DB -f dump.sql
GUI データベース管理ツールについては、DBeaver vs Beekeeper - SQL データベース管理ツール および Linux への DBeaver のインストール - 手順 を参照してください。
レプリケーション(概要)
- WAL ファイルは プライマリ → スタンバイ にストリーミングされます
- 重要な設定:
wal_level,max_wal_senders,hot_standby,primary_conninfo - ツール:
pg_basebackup,standby.signal(PG ≥12)
注意点とヒント
- インデックスと演算子には常に
jsonではなくjsonbを使用してください。 timestamptz(タイムゾーン認識型) を優先してください。DISTINCT ONは、Postgres において「グループごとの上位 N 件」を取得するための強力な機能です。serialの代わりにGENERATED ALWAYS AS IDENTITYを使用してください。- 本番環境のクエリでは
SELECT *を避けてください。 - 選択的なフィルタと結合キーにはインデックスを作成してください。
- 最適化する前に
EXPLAIN (ANALYZE)で測定してください。
バージョン固有の機能(v12 以降)
-- 生成された ID
CREATE TABLE t (id bigINT GENERATED ALWAYS AS IDENTITY, ...);
-- 部分インデックスによる UPSERT
CREATE UNIQUE INDEX ON t (key) WHERE is_active;