はじめに

データベースを活用するアプリケーション開発において、データの取得(SELECT)だけでなく、データの追加・更新・削除も不可欠な操作です。ユーザー登録、商品情報の更新、不要データの削除など、日常的に発生するこれらの操作を正しく理解することが、信頼性の高いシステム構築への第一歩となります。

本記事では、PostgreSQLにおけるINSERT文、UPDATE文、DELETE文の基本構文から応用的な使い方、さらにデータの整合性を保証するトランザクション(BEGIN/COMMIT/ROLLBACK)まで、実践的なサンプルコードとともに解説します。

この記事を読むことで、以下のことができるようになります。

  • INSERT文を使ってテーブルにデータを追加できる(単一行、複数行、SELECT結果の挿入)
  • UPDATE文で既存データを条件付きで更新し、RETURNING句で更新結果を取得できる
  • DELETE文でデータを削除し、TRUNCATEとの使い分けを理解できる
  • トランザクションを使ってデータの整合性を保証できる

前提条件

  • PostgreSQL 14以降がインストールされていること
  • psqlまたはGUIツール(pgAdmin、DBeaver等)でデータベースに接続できること
  • SELECT文の基本的な構文を理解していること

サンプルデータの準備

本記事で使用するサンプルテーブルを準備します。以下のSQLを実行してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- 社員テーブルの作成
CREATE TABLE employees (
    employee_id   SERIAL PRIMARY KEY,
    name          VARCHAR(100) NOT NULL,
    department    VARCHAR(50) NOT NULL,
    salary        INTEGER NOT NULL,
    hire_date     DATE DEFAULT CURRENT_DATE,
    is_active     BOOLEAN DEFAULT TRUE,
    created_at    TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at    TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 部署テーブルの作成
CREATE TABLE departments (
    department_id   SERIAL PRIMARY KEY,
    department_name VARCHAR(50) NOT NULL UNIQUE,
    budget          INTEGER DEFAULT 0,
    manager_id      INTEGER
);

-- 監査ログテーブルの作成
CREATE TABLE audit_logs (
    log_id        SERIAL PRIMARY KEY,
    table_name    VARCHAR(50) NOT NULL,
    operation     VARCHAR(10) NOT NULL,
    old_data      JSONB,
    new_data      JSONB,
    executed_at   TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT文によるデータの追加

INSERT文は、テーブルに新しい行(レコード)を追加するためのSQL文です。PostgreSQLでは、単一行の挿入から複数行の一括挿入、さらにはSELECT結果の挿入まで、柔軟なデータ追加が可能です。

基本構文

INSERT文の基本構文は以下の通りです。

1
2
INSERT INTO テーブル名 (カラム名1, カラム名2, ...)
VALUES (1, 2, ...);

単一行の挿入

最も基本的なINSERT文は、1行のデータを追加する形式です。カラム名を明示的に指定することで、値の順序を柔軟に設定できます。

1
2
3
-- カラム名を指定して挿入
INSERT INTO employees (name, department, salary)
VALUES ('田中太郎', '開発部', 450000);

実行結果として、INSERT 0 1というメッセージが表示されます。これは1行が正常に挿入されたことを示しています。

カラム名を省略することも可能ですが、その場合はテーブル定義の順序で全てのカラムに値を指定する必要があります。

1
2
3
-- カラム名を省略(非推奨)
INSERT INTO employees
VALUES (DEFAULT, '山田花子', '営業部', 380000, DEFAULT, DEFAULT, DEFAULT, DEFAULT);

カラム名を明示的に指定する方法を推奨します。この方法であれば、テーブル構造が変更されてもSQLの修正が最小限で済み、コードの可読性も向上します。

DEFAULTキーワードの活用

DEFAULT値が設定されているカラムには、DEFAULTキーワードを使用することで明示的にデフォルト値を適用できます。

1
2
3
4
5
6
7
8
-- DEFAULTキーワードを使用
INSERT INTO employees (name, department, salary, hire_date)
VALUES ('佐藤次郎', '人事部', 420000, DEFAULT);

-- 確認
SELECT employee_id, name, department, salary, hire_date
FROM employees
WHERE name = '佐藤次郎';

実行結果は以下のようになります。

employee_id name department salary hire_date
3 佐藤次郎 人事部 420000 2026-01-03

hire_dateにはDEFAULT値であるCURRENT_DATE(実行日)が自動的に設定されています。

複数行の一括挿入

VALUES句に複数の値セットをカンマで区切ることで、1つのINSERT文で複数行を同時に挿入できます。

1
2
3
4
5
6
7
-- 複数行の一括挿入
INSERT INTO employees (name, department, salary)
VALUES
    ('鈴木一郎', '開発部', 520000),
    ('高橋美咲', '開発部', 480000),
    ('伊藤健太', '営業部', 400000),
    ('渡辺真理', '人事部', 390000);

この方法には以下のメリットがあります。

観点 単一INSERTを複数回 複数行INSERT
ネットワーク往復 行数分発生 1回のみ
トランザクション 個別または明示的に管理 1つのトランザクション
パフォーマンス 低い 高い

大量のデータを挿入する場合は、複数行INSERTを使用することでパフォーマンスが大幅に向上します。

SELECT結果の挿入

INSERT … SELECT構文を使用すると、別のテーブルやクエリの結果をそのままINSERTできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- 部署データの挿入
INSERT INTO departments (department_name, budget)
VALUES
    ('開発部', 50000000),
    ('営業部', 30000000),
    ('人事部', 20000000);

-- 開発部の社員を別テーブルにコピーする例
CREATE TABLE dev_team_backup AS SELECT * FROM employees WHERE 1=0;

INSERT INTO dev_team_backup
SELECT * FROM employees
WHERE department = '開発部';

この構文は、データのバックアップ、データ移行、集計結果の保存などに活用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
-- 部署ごとの人数を集計テーブルに挿入
CREATE TABLE department_summary (
    department VARCHAR(50) PRIMARY KEY,
    employee_count INTEGER,
    total_salary BIGINT,
    summarized_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO department_summary (department, employee_count, total_salary)
SELECT
    department,
    COUNT(*),
    SUM(salary)
FROM employees
GROUP BY department;

RETURNING句による挿入結果の取得

RETURNING句を使用すると、INSERT文の実行結果として挿入された行のデータを取得できます。これは、自動採番されたIDやDEFAULT値を確認したい場合に特に有用です。

1
2
3
4
-- RETURNING句で挿入されたデータを取得
INSERT INTO employees (name, department, salary)
VALUES ('中村優子', '開発部', 460000)
RETURNING employee_id, name, created_at;

実行結果は以下のようになります。

employee_id name created_at
8 中村優子 2026-01-03 20:15:30.123456

RETURNING句では、アスタリスク(*)を使用して全カラムを取得することも可能です。

1
2
3
INSERT INTO employees (name, department, salary)
VALUES ('小林大輔', '営業部', 410000)
RETURNING *;

ON CONFLICT句によるUPSERT

PostgreSQLのON CONFLICT句を使用すると、一意制約に違反した場合の代替動作を指定できます。これにより、「存在しなければ挿入、存在すれば更新(UPSERT)」という処理を1つのSQL文で実現できます。

1
2
3
4
5
6
7
8
-- 部署テーブルへのUPSERT
INSERT INTO departments (department_name, budget)
VALUES ('開発部', 55000000)
ON CONFLICT (department_name)
DO UPDATE SET budget = EXCLUDED.budget;

-- 確認
SELECT * FROM departments WHERE department_name = '開発部';

EXCLUDEDは挿入しようとした値を参照するための特別なテーブル名です。

ON CONFLICT句には2つのアクションを指定できます。

アクション 説明
DO NOTHING 競合時は何もせず、エラーも発生しない
DO UPDATE SET 競合時に既存行を更新する
1
2
3
4
5
-- DO NOTHING の例
INSERT INTO departments (department_name, budget)
VALUES ('開発部', 60000000)
ON CONFLICT (department_name)
DO NOTHING;

UPDATE文によるデータの更新

UPDATE文は、テーブル内の既存データを変更するためのSQL文です。WHERE句で対象行を絞り込むことで、特定の条件に一致するデータのみを更新できます。

基本構文

UPDATE文の基本構文は以下の通りです。

1
2
3
UPDATE テーブル名
SET カラム名1 = 1, カラム名2 = 2, ...
WHERE 条件;

WHERE句を省略すると、テーブル内の全ての行が更新されるため、十分注意が必要です。

単一カラムの更新

最も基本的なUPDATE文は、1つのカラムを更新する形式です。

1
2
3
4
-- 特定社員の給与を更新
UPDATE employees
SET salary = 500000
WHERE name = '田中太郎';

実行結果として、UPDATE 1というメッセージが表示されます。これは1行が更新されたことを示しています。

複数カラムの同時更新

SET句にカンマで区切って複数のカラムを指定することで、同時に複数のカラムを更新できます。

1
2
3
4
5
6
7
-- 複数カラムの同時更新
UPDATE employees
SET
    department = '企画部',
    salary = 550000,
    updated_at = CURRENT_TIMESTAMP
WHERE employee_id = 1;

計算式を使った更新

SET句では、現在の値を使った計算式を指定できます。

1
2
3
4
5
6
7
8
9
-- 全社員の給与を5%アップ
UPDATE employees
SET salary = salary * 1.05
WHERE is_active = TRUE;

-- 特定部署の給与を10000円アップ
UPDATE employees
SET salary = salary + 10000
WHERE department = '開発部';

条件付き更新

複雑な条件を指定することで、より精密な更新が可能です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- 給与が400000未満かつ開発部の社員の給与を400000に引き上げ
UPDATE employees
SET salary = 400000
WHERE salary < 400000
  AND department = '開発部';

-- サブクエリを使った条件指定
UPDATE employees
SET is_active = FALSE
WHERE department IN (
    SELECT department_name
    FROM departments
    WHERE budget < 25000000
);

FROMを使った他テーブル参照

PostgreSQL独自の拡張として、FROM句を使って他のテーブルを参照した更新が可能です。

1
2
3
4
5
6
-- 部署テーブルから予算情報を取得して更新
UPDATE employees e
SET salary = salary + (d.budget / 100)
FROM departments d
WHERE e.department = d.department_name
  AND d.budget > 40000000;

この構文は標準SQLではありませんが、サブクエリを使う方法よりも直感的で可読性が高くなる場合があります。

RETURNING句による更新結果の取得

UPDATE文でもRETURNING句を使用できます。更新前後の値を確認する場合に特に有用です。

1
2
3
4
5
-- 更新後の値を取得
UPDATE employees
SET salary = salary + 20000
WHERE department = '営業部'
RETURNING employee_id, name, salary;

PostgreSQL 18以降では、OLDNEWを使って更新前後の値を同時に取得できます。

1
2
3
4
5
-- 更新前後の値を同時に取得(PostgreSQL 18以降)
UPDATE employees
SET salary = salary + 30000
WHERE employee_id = 2
RETURNING OLD.salary AS old_salary, NEW.salary AS new_salary, name;

PostgreSQL 17以前のバージョンでは、この機能は利用できません。その場合は、トランザクションと複数のクエリを組み合わせて同様の結果を得ることができます。

UPDATE時の注意点

UPDATE文を実行する際は、以下の点に注意してください。

注意点 説明
WHERE句の確認 WHERE句を忘れると全行が更新される
対象行の事前確認 同じWHERE条件でSELECTを実行して対象を確認する
トランザクションの活用 重要な更新はトランザクション内で実行する
更新行数の確認 実行結果のUPDATE countを確認する
1
2
3
4
5
6
7
8
9
-- 更新前に対象行を確認する習慣
SELECT employee_id, name, salary
FROM employees
WHERE department = '開発部';

-- 確認後に更新を実行
UPDATE employees
SET salary = salary + 50000
WHERE department = '開発部';

DELETE文によるデータの削除

DELETE文は、テーブルから行を削除するためのSQL文です。WHERE句で削除対象を指定し、条件に一致する行のみを削除します。

基本構文

DELETE文の基本構文は以下の通りです。

1
2
DELETE FROM テーブル名
WHERE 条件;

UPDATE文と同様に、WHERE句を省略すると全ての行が削除されるため、細心の注意が必要です。

条件を指定した削除

WHERE句を使用して、特定の条件に一致する行のみを削除します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- 特定の社員を削除
DELETE FROM employees
WHERE employee_id = 5;

-- 非アクティブな社員を削除
DELETE FROM employees
WHERE is_active = FALSE;

-- 複合条件での削除
DELETE FROM employees
WHERE department = '人事部'
  AND salary < 400000;

USINGを使った他テーブル参照

PostgreSQL独自の拡張として、USING句を使って他のテーブルを参照した削除が可能です。

1
2
3
4
5
-- 予算が少ない部署の社員を削除
DELETE FROM employees e
USING departments d
WHERE e.department = d.department_name
  AND d.budget < 15000000;

標準SQLでは、サブクエリを使って同様の処理を行います。

1
2
3
4
5
6
7
-- サブクエリを使った削除(標準SQL準拠)
DELETE FROM employees
WHERE department IN (
    SELECT department_name
    FROM departments
    WHERE budget < 15000000
);

RETURNING句による削除結果の取得

DELETE文でもRETURNING句を使用して、削除された行の情報を取得できます。これは監査ログの記録や、削除されたデータの確認に役立ちます。

1
2
3
4
5
6
7
8
9
-- 削除されたデータを取得
DELETE FROM employees
WHERE is_active = FALSE
RETURNING *;

-- 特定のカラムのみ取得
DELETE FROM employees
WHERE employee_id = 10
RETURNING employee_id, name, department;

全行削除とTRUNCATEの違い

テーブルの全行を削除する方法には、DELETE文とTRUNCATE文の2つがあります。

1
2
3
4
5
-- DELETE文で全行削除
DELETE FROM audit_logs;

-- TRUNCATE文で全行削除
TRUNCATE TABLE audit_logs;

両者には以下のような違いがあります。

特性 DELETE TRUNCATE
削除単位 行ごとに削除 テーブル全体を一括削除
速度 大量データでは遅い 非常に高速
WHERE句 使用可能 使用不可(全行のみ)
トランザクション ロールバック可能 PostgreSQLではロールバック可能
トリガー 行ごとに発火 発火しない
RETURNING句 使用可能 使用不可
外部キー制約 個別に確認 CASCADEオプションで対応

TRUNCATEの実行例を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 基本的なTRUNCATE
TRUNCATE TABLE audit_logs;

-- 複数テーブルを同時にTRUNCATE
TRUNCATE TABLE audit_logs, department_summary;

-- 外部キー制約がある場合(参照先も含めて削除)
TRUNCATE TABLE departments CASCADE;

-- シーケンスもリセット
TRUNCATE TABLE employees RESTART IDENTITY;

RESTART IDENTITYオプションを指定すると、SERIAL型などで使用されているシーケンスの値もリセットされます。

DELETE時の注意点

DELETE文を実行する際は、以下の点に注意してください。

注意点 説明
外部キー制約 参照されている行は削除できない
カスケード削除 参照先のデータも連鎖的に削除される可能性
物理削除 vs 論理削除 実運用では論理削除(is_activeフラグ)が推奨されることも
削除前のバックアップ 重要なデータは事前にバックアップを取得
1
2
3
4
-- 外部キー制約エラーの例
-- departmentsテーブルがemployeesから参照されている場合
DELETE FROM departments WHERE department_name = '開発部';
-- ERROR: update or delete on table "departments" violates foreign key constraint

トランザクションの基礎

トランザクションは、データベースにおいて複数の操作を1つの論理的な作業単位としてまとめる仕組みです。トランザクションを使用することで、データの整合性と一貫性を保証できます。

トランザクションの概念

トランザクションには、ACID特性と呼ばれる4つの重要な性質があります。

特性 英語 説明
原子性 Atomicity 全ての操作が成功するか、全て失敗するか(all or nothing)
一貫性 Consistency トランザクション前後でデータの整合性が保たれる
分離性 Isolation 並行するトランザクション間で互いの処理が見えない
永続性 Durability コミット後のデータは永続的に保存される

以下の図は、トランザクションの基本的な流れを示しています。

sequenceDiagram
    participant App as アプリケーション
    participant DB as PostgreSQL

    App->>DB: BEGIN
    DB-->>App: トランザクション開始

    App->>DB: UPDATE accounts SET balance = balance - 10000 WHERE id = 1
    DB-->>App: 更新成功(未確定)

    App->>DB: UPDATE accounts SET balance = balance + 10000 WHERE id = 2
    DB-->>App: 更新成功(未確定)

    alt 全て成功
        App->>DB: COMMIT
        DB-->>App: トランザクション確定
    else エラー発生
        App->>DB: ROLLBACK
        DB-->>App: トランザクション取り消し
    end

BEGIN/COMMIT/ROLLBACK

トランザクションは、BEGIN文で開始し、COMMIT文で確定、ROLLBACK文で取り消しを行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- トランザクションの基本的な流れ
BEGIN;

-- 複数の操作を実行
UPDATE employees
SET salary = salary - 50000
WHERE employee_id = 1;

UPDATE employees
SET salary = salary + 50000
WHERE employee_id = 2;

-- 問題がなければ確定
COMMIT;

エラーが発生した場合やデータを取り消したい場合は、ROLLBACK文を使用します。

1
2
3
4
5
6
7
8
9
BEGIN;

UPDATE employees
SET salary = salary - 100000
WHERE employee_id = 1;

-- 何らかの理由で取り消したい
ROLLBACK;
-- salary は変更されていない状態に戻る

実践的なトランザクション例

銀行の送金処理は、トランザクションの典型的な使用例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
-- 銀行の送金処理
BEGIN;

-- 送金元から引き落とし
UPDATE accounts
SET balance = balance - 50000
WHERE account_id = 'A001'
  AND balance >= 50000;  -- 残高チェック

-- 更新された行数を確認(アプリケーション側で判定)
-- 0行の場合は残高不足

-- 送金先に入金
UPDATE accounts
SET balance = balance + 50000
WHERE account_id = 'B002';

COMMIT;

SAVEPOINTによる部分的なロールバック

SAVEPOINTを使用すると、トランザクション内の特定の地点まで部分的にロールバックできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
BEGIN;

-- 最初の操作
INSERT INTO employees (name, department, salary)
VALUES ('テスト太郎', '開発部', 400000);

-- セーブポイントを設定
SAVEPOINT sp1;

-- 次の操作
INSERT INTO employees (name, department, salary)
VALUES ('テスト次郎', '開発部', 'invalid');  -- エラー発生

-- sp1まで戻る(最初のINSERTは維持)
ROLLBACK TO sp1;

-- 別の操作を実行
INSERT INTO employees (name, department, salary)
VALUES ('テスト次郎', '開発部', 420000);

COMMIT;

SAVEPOINTの活用シーンには以下のようなものがあります。

シーン 説明
部分的なエラー回復 一部の処理が失敗しても全体を取り消さない
試行的な操作 結果を見て続行か取り消しかを判断
バッチ処理 一部のデータでエラーが発生しても処理を継続
flowchart TD
    A[BEGIN] --> B[INSERT 社員A]
    B --> C[SAVEPOINT sp1]
    C --> D{INSERT 社員B}
    D -->|成功| E[SAVEPOINT sp2]
    D -->|失敗| F[ROLLBACK TO sp1]
    F --> G[INSERT 社員B 修正版]
    E --> H[INSERT 社員C]
    G --> H
    H --> I[COMMIT]

自動コミットとトランザクションブロック

PostgreSQLでは、明示的にBEGIN文を発行しない場合、各SQL文は個別のトランザクションとして自動的にコミットされます(自動コミットモード)。

1
2
3
4
5
-- 自動コミット(各文が個別にコミット)
INSERT INTO employees (name, department, salary) VALUES ('A', '開発部', 400000);
-- ここで自動コミット
INSERT INTO employees (name, department, salary) VALUES ('B', '開発部', 410000);
-- ここで自動コミット

複数の操作をまとめたい場合は、明示的にトランザクションブロックを使用します。

1
2
3
4
5
6
-- トランザクションブロック(明示的)
BEGIN;
INSERT INTO employees (name, department, salary) VALUES ('C', '開発部', 420000);
INSERT INTO employees (name, department, salary) VALUES ('D', '開発部', 430000);
COMMIT;
-- ここで両方の挿入がコミット

トランザクションのベストプラクティス

トランザクションを効果的に活用するためのベストプラクティスをまとめます。

プラクティス 説明
短時間で完了 長時間のトランザクションはロック競合の原因になる
必要最小限のスコープ 関連する操作のみをトランザクションに含める
エラーハンドリング エラー時は必ずROLLBACKを実行
デッドロック対策 テーブルのアクセス順序を統一する
明示的なトランザクション 重要な操作はBEGIN/COMMITを明示する
1
2
3
4
5
6
-- 良い例:関連する操作のみをトランザクションに含める
BEGIN;
UPDATE orders SET status = 'shipped' WHERE order_id = 100;
UPDATE inventory SET stock = stock - 1 WHERE product_id = 50;
INSERT INTO shipping_logs (order_id, shipped_at) VALUES (100, CURRENT_TIMESTAMP);
COMMIT;

実践的な活用例

ここまで学んだINSERT、UPDATE、DELETE、トランザクションを組み合わせた実践的な例を紹介します。

監査ログ付きの更新処理

データを更新する際に、変更前後の値を監査ログに記録する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
BEGIN;

-- 更新前のデータを取得
INSERT INTO audit_logs (table_name, operation, old_data, new_data)
SELECT
    'employees',
    'UPDATE',
    to_jsonb(e.*),
    NULL
FROM employees e
WHERE employee_id = 1;

-- データを更新
UPDATE employees
SET salary = 600000, updated_at = CURRENT_TIMESTAMP
WHERE employee_id = 1;

-- 更新後のデータで監査ログを更新
UPDATE audit_logs
SET new_data = (
    SELECT to_jsonb(e.*)
    FROM employees e
    WHERE employee_id = 1
)
WHERE log_id = (SELECT MAX(log_id) FROM audit_logs);

COMMIT;

バッチ処理でのエラーハンドリング

複数のデータを処理する際に、一部でエラーが発生しても処理を継続する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
BEGIN;

-- 最初のデータ処理
SAVEPOINT batch_sp;
INSERT INTO employees (name, department, salary) VALUES ('バッチ1', '開発部', 400000);

-- 2番目のデータ処理(エラーを想定)
SAVEPOINT batch_sp;
BEGIN
    INSERT INTO employees (name, department, salary) VALUES ('バッチ2', '開発部', 'error');
EXCEPTION WHEN OTHERS THEN
    ROLLBACK TO batch_sp;
END;

-- 3番目のデータ処理
SAVEPOINT batch_sp;
INSERT INTO employees (name, department, salary) VALUES ('バッチ3', '開発部', 420000);

COMMIT;

注意:上記のEXCEPTION構文はPL/pgSQL(ストアドファンクション内)で使用するものです。psqlから直接実行する場合は、エラー発生時に手動でROLLBACK TOを実行します。

UPSERTと更新ログの記録

データが存在すれば更新、存在しなければ挿入し、その結果をログに記録する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- UPSERTの結果をログに記録
WITH upsert_result AS (
    INSERT INTO departments (department_name, budget)
    VALUES ('マーケティング部', 25000000)
    ON CONFLICT (department_name)
    DO UPDATE SET budget = EXCLUDED.budget
    RETURNING *, (xmax = 0) AS is_insert
)
INSERT INTO audit_logs (table_name, operation, new_data)
SELECT
    'departments',
    CASE WHEN is_insert THEN 'INSERT' ELSE 'UPDATE' END,
    to_jsonb(upsert_result.*)
FROM upsert_result;

xmax = 0はPostgreSQL内部のシステムカラムを利用したテクニックで、新規挿入された行かどうかを判定しています。

まとめ

本記事では、PostgreSQLにおけるデータ操作の基本であるINSERT文、UPDATE文、DELETE文、およびトランザクション管理について解説しました。

主要なポイント

操作 主要構文 重要なオプション
INSERT INSERT INTO … VALUES RETURNING、ON CONFLICT
UPDATE UPDATE … SET … WHERE RETURNING、FROM
DELETE DELETE FROM … WHERE RETURNING、USING
TRUNCATE TRUNCATE TABLE RESTART IDENTITY、CASCADE
トランザクション BEGIN / COMMIT / ROLLBACK SAVEPOINT

次のステップ

データ操作の基本を習得した後は、以下のトピックを学ぶことで、より高度なデータベース操作が可能になります。

  • JOINによるテーブル結合:複数テーブルを組み合わせた複雑なデータ取得
  • インデックス:UPDATE/DELETE操作のパフォーマンス最適化
  • ストアドプロシージャ:複雑なビジネスロジックのデータベース側実装
  • トリガー:データ変更時の自動処理

本記事で紹介した構文とテクニックを実際のプロジェクトで活用し、データベース操作のスキルを磨いてください。

参考リンク