「完全攻略 PostgreSQL: 現場で使える最強データベース入門 」という書籍をベースに、自分なりに気になったことやよく使いそうな機能をまとめていきます。
作業リポジトリはこちら:
- 【PostgreSQL 総復習】1. psqlコマンドライン操作
- 【PostgreSQL 総復習】2. データベース・スキーマ・ユーザー
- 【PostgreSQL 総復習】3. データ型・CREATE TABLE
- 【PostgreSQL 総復習】4. SELECT・ウィンドウ関数・CTE (WITH句)
- 【PostgreSQL 総復習】5. INSERT・UPDATE・DELETE・RETURNING
- 【PostgreSQL 総復習】6. トランザクションとACID特性
- 【PostgreSQL 総復習】7. インデックス
トランザクション
トランザクションはデータベースの一連の操作を「一つのまとまり」として扱う機構であり、ACID特性はトランザクションの品質を保証する基本原則です。
トランザクションの基本
トランザクションは、データベース内の一連の処理をまとめた単位で、トランザクションの結果は「すべて成功」」か「すべて失敗」かのどちらかの状態を取ります。
途中で処理に失敗した場合はそれまでの操作がすべて取り消されるため、トランザクションの開始前と終了後でデータベースは整合性のある状態を保ちます。
トランザクションの開始は BEGIN 、終了は COMMIT 、取り消しは ROLLBACK で行います。
基本構文
-- トランザクションを COMMIT で確定させる
BEGIN;
INSERT INTO users ...
UPDATE users ...
COMMIT;
-- トランザクションを ROLLBACK で取り消す (トランザクション内の変更は全て取消され、開始前の状態に戻る)
BEGIN;
INSERT INTO users ...
UPDATE users ...
ROLLBACK;
サンプル
BEGIN;
WITH
-- userの登録
ins_user AS (
INSERT INTO users (email, name, age, gender)
VALUES ('ryohei.shibata@example.com', '柴田亮平', 25, 'male')
ON CONFLICT (email) DO UPDATE
SET name = EXCLUDED.name,
age = EXCLUDED.age,
gender = EXCLUDED.gender
RETURNING id
),
-- 新規注文の作成
new_order AS (
INSERT INTO orders (user_id, total_amount)
SELECT id, 0
FROM ins_user
RETURNING id
)
-- NOTE: PostgreSQLでは、1つのSQLステートメント(WITH句を含む全体)が実行されるとき、ステートメント開始時点のスナップショットが取得されます
-- なので直接ordersを指定するとスナップショットを参照することになり、INSERT結果が反映されていません
SELECT * FROM new_order;
-- id
-- --------------------------------------
-- 059280d6-3b92-45e7-ab7e-18a5c7a2219e
WITH
-- 新規注文の取得
new_order AS (
SELECT id
FROM orders
WHERE id = '059280d6-3b92-45e7-ab7e-18a5c7a2219e'
),
-- 注文する商品の選択 (ランダム)
selected_products AS (
SELECT id, price, discount
FROM products
ORDER BY random()
LIMIT 3
),
-- 商品を注文に紐づける
ins_items AS (
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
SELECT
new_order.id,
selected_products.id,
1,
selected_products.price * (100 - COALESCE(selected_products.discount, 0)) / 100.0
FROM new_order
CROSS JOIN selected_products
RETURNING order_id, product_id, quantity, unit_price
),
-- 商品の値段と割引率から合計金額を求める
recalc AS (
SELECT
order_id,
SUM(unit_price * quantity) AS total_amount
FROM ins_items
GROUP BY order_id
)
-- 注文の合計金額を更新する
UPDATE orders
SET total_amount = recalc.total_amount,
status = 'pending'
FROM recalc
WHERE orders.id = recalc.order_id
RETURNING orders.*;
-- id | user_id | status | total_amount | ordered_at | updated_at
-- --------------------------------------+---------+---------+--------------+-------------------------------+-------------------------------
-- 059280d6-3b92-45e7-ab7e-18a5c7a2219e | 62 | pending | 22128.00 | 2026-02-17 03:28:40.711309+00 | 2026-02-17 03:28:40.711309+00
-- COMMIT もしくは ROLLBACK
COMMIT;
-- ROLLBACK;
SELECT * FROM orders WHERE id = '059280d6-3b92-45e7-ab7e-18a5c7a2219e';
-- id | user_id | status | total_amount | ordered_at | updated_at
-- --------------------------------------+---------+---------+--------------+-------------------------------+-------------------------------
-- 059280d6-3b92-45e7-ab7e-18a5c7a2219e | 62 | pending | 22128.00 | 2026-02-17 03:28:40.711309+00 | 2026-02-17 03:28:40.711309+00
トランザクション内でエラーが発生した場合の挙動
-
BEGINでトランザクション開始。 - いくつかのSQL成功(まだ確定していない)。
- エラー発生(Syntax Errorや制約違反など)。
- トランザクションが 「中断(Aborted)」 状態になる。
- これ以降、正しいSQLを投げても
ERROR: current transaction is aborted...と返され、一切処理されない。 -
ROLLBACK(またはCOMMIT)を実行すると、トランザクションが終了し、最初の成功したSQLも含めて全て無かったことになる(ロールバックされる)。
ACID特性
- Atomicity (原子性) : トランザクション内の処理は「すべて成功」「すべて失敗」のいずれか
- Consistency (一貫性) : トランザクションの開始前と終了後で、データベースは整合性のある状態を保つ。
- Isolation (独立性) : 同時実行されるトランザクションはお互いに干渉せず、各トランザクションは独立して実行される。
- Durability (永続性) : トランザクションがコミットされた後、その変更は永続的に保存される。
Atomicity (原子性)
トランザクション内の処理は「すべて成功」「すべて失敗」のいずれかで、途中で失敗するとそれまでの操作はすべて取り消されます。
処理の途中で障害が発生してもデータベースは不整合な状態になりません。
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 2;
COMMIT;
Consistency (一貫性)
一貫性はデータベースが定義された制約に従っている状態を保つことを意味します。
制約違反や整合性の破壊が起きないように、トランザクションは常にルールをチェックします。
-- この制約に違反するする更新は、トランザクション全体が失敗する
ALTER TABLE users ADD CONSTRAINT email UNIQUE;
Isolation (独立性)
Isolation は複数のトランザクションが同時に実行される場合でもお互いに鑑賞しない性質です。
PostgreSQLの4種類のトランザクション:
- Read Uncommitted : 他のトランザクションのみコミット変更も読み込める
- Read Committed : 他のトランザクションがコミットした変更のみ読み込む (default)
- Repeatable Read : トランザクション開始時の状態を維持し、一貫した読み込みを保証する
- Serializable : 完全に独立したトランザクションを実現する
-- Isolationレベルの設定
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT * FROM users WHERE id = 1;
COMMIT;
Durability (永続性)
Durability は一度コミットされたトランザクションは永続的に保存される性質です。
PostgreSQLはWAL (Write-Ahead Logging) によってトランザクションログを管理し、障害発生時でもデータを復旧できます。
トランザクション制御の実務活用
- 複数操作の一括実行 : INSERT, UPDATE, DELETEなどをまとめて行い整合性を保ちながら処理をする
- エラー時のロールバック : エラー発生時にROLLBACKで変更を取り消す
- 分離レベルの適切な設定 : 競合の可能性がある処理には高い分離レベルを設定する
- トランザクションの短期化 : 長期のトランザクションはロック競合やパフォーマンス低下を招くためできる限り短くする
PostgreSQLのトランザクション特有機能
SAVEPOINT
トランザクション内にポイントを設定して、一部のみロールバックを可能にします。
BEGIN;
SAVEPOINT save1;
UPDATE employees SET salary = salary * 1.05 WHERE department_id = 1;
ROLLBACK TO save1; -- 部分ロールバック
COMMIT;
AUTOCOMMIT (デフォルトで有効)
各SQLが自動的に独立したトランザクションとして扱われます