データベースの教科書

SELECT から JOIN、ウィンドウ関数、ER 図、正規化、インデックス設計まで。データベースの基礎と SQL、そしてデータモデル設計・性能チューニングの勘所を RDBMS 横断で体系的にまとめました。

カテゴリから探す

目的に合わせてカテゴリを選んでください

設計ガイド

正規化・インデックス・実行計画など、設計と性能のベストプラクティスを解説

データ設計が重要な理由

データ設計は一度決めると直せない。その理由を 5 つの事実で示す。
正規化とは — 1NF〜5NF / BCNF の概要

正規化が何を解決するのか、各正規形が排除する問題を一覧で俯瞰する導入ページ。
外部キー制約の使いどころと ON DELETE オプション

整合性保証と ON DELETE CASCADE / SET NULL / RESTRICT の使い分け。
代理キー vs 自然キー — どちらを主キーにすべきか

代理キー(連番・UUID)と自然キー(メール・ISBN)の使い分け早見表。
ER 図の書き方・読み方

IE 記法(カラスの足)で ER 図を読み書きする基礎。多重度・描き方・ツール選びまで。
設計時にインデックスを考える理由 — 後付けが高コストになる

「遅くなったら貼る」では間に合わない。設計時にどのクエリをどの索引で支えるかを決める。
複合インデックスの列順設計 — 等値 → 範囲 → ソート

複合インデックスの列順は「等値 → 範囲 → ソート」。左端一致の再利用とよくある失敗を押さえる。
実行計画の読み方 — 基礎編

実行計画の基礎。Seq Scan・Index Scan・各種 JOIN など RDBMS 共通の演算子と読み方。
N+1 問題 — ORM ループの静かな殺し屋

ORM で頻発する N+1 問題の典型パターンと、eager loading・JOIN・IN バッチによる解消法。
スロークエリの特定と対処 — slow log / pg_stat_statements

遅いクエリを特定する方法。MySQL スロークエリログ・PostgreSQL pg_stat_statements・auto_explain。

トラブルシューティング

初心者がハマりやすいポイントと解決策

重複行を消したい — DISTINCT / GROUP BY / ROW_NUMBER() 使い分け

重複行を取り除きたいとき。DISTINCT / GROUP BY / ROW_NUMBER の使い分け。
グループごとに最新1件を取得する (Greatest-N-per-group)

ユーザーごとの最新注文など、グループ内で最新1件を取りたいとき。
グループごとの上位 N 件 — ROW_NUMBER / RANK / DENSE_RANK

カテゴリごとの売上上位3件など、各グループで上位N件を取りたいとき。
行を列に変換(ピボット) — CASE / PIVOT / crosstab

月別売上を月ごとの列に並べたいとき。行を列に変換するピボット処理。
階層構造を扱う — 隣接リスト / 再帰CTE / 経路列挙 / 入れ子集合

カテゴリツリーや組織図など、階層データを保存・検索したいとき。
連続した期間・グループを検出する (Gaps and Islands)

連続ログイン日数や欠番の検出など、連続する区間を見つけたいとき。
累計を計算する — SUM() OVER (ORDER BY ...)

日次売上の累計や残高推移など、積み上げ計算を出したいとき。
ページネーション — OFFSET の罠と keyset pagination

大量データで OFFSET ページングが遅いとき。keyset 方式で一定速度に。
ランダムサンプリング — ORDER BY RANDOM() の罠と代替

ランダムに N 件取りたいとき。ORDER BY RANDOM() より速い方法。
大量 INSERT を速くする — multi-row VALUES / COPY / LOAD DATA

大量データを高速に投入したいとき。単行 INSERT のループが遅い理由と対処。

よく見られているトピック

まずはここからチェック

SELECT
テーブルからデータを取り出す最も基本の句。取り出す列の指定(ワイルドカード/明示列)、エイリアス、計算列の書き方を押さえる
WHERE
行を絞り込む句。比較・論理演算子、IN・BETWEEN・LIKE、NULL 扱いの注意点を押さえる
CTE (WITH 句)
WITH 句で名前付きの一時結果を定義。可読性向上、再帰 CTE で階層構造、MATERIALIZED ヒントにも触れる
INNER JOIN
両側のテーブルに一致する行だけを返す結合。最も基本的な結合方法
GROUP BY
行をグループ化して集約関数を適用する。SELECT に書ける列の制約や NULL の扱いを理解しよう
COUNT / SUM / AVG / MIN / MAX
基本の集約関数。COUNT(*) と COUNT(col) の違い、NULL を無視する性質を理解する
ウィンドウ関数入門 (OVER / PARTITION BY)
GROUP BY と違い行数を潰さず集計する。OVER と PARTITION BY の基本、移動平均の例
INSERT
テーブルへの行追加。基本形、複数行まとめて、SELECT 結果から、DEFAULT VALUES まで
UPDATE
既存行の値を書き換える。UPDATE ... FROM / JOIN と、WHERE 忘れによる全件更新事故
MERGE / UPSERT
存在すれば UPDATE、なければ INSERT。標準 MERGE と PostgreSQL / MySQL の方言
CREATE TABLE
テーブルを定義する基本 DDL。列名・データ型・DEFAULT・NOT NULL・コメントの書き方を整理
BEGIN / COMMIT / ROLLBACK
トランザクションの開始・確定・取り消しを行う基本コマンド。ACID の I (独立性) を支える仕組み
分離レベル (Isolation Levels)
READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE の 4 段階と発生する異常現象
件数制限の方言 (LIMIT / TOP / FETCH)
SELECT の返却行数を制限する書き方は RDBMS ごとに異なる。LIMIT / TOP / FETCH FIRST の違いと移植のコツを解説
UPSERT の方言 (ON CONFLICT / ON DUPLICATE / MERGE)
存在すれば UPDATE、無ければ INSERT の UPSERT は RDBMS で記法が大きく異なる。PostgreSQL/MySQL/SQLite/SQL Server の比較
日付関数の方言 (NOW / DATE_ADD / TO_CHAR)
現在時刻・日付加算・フォーマットは RDBMS 差分が大きい。NOW / GETDATE / CURRENT_TIMESTAMP、INTERVAL / DATE_ADD / DATEADD を比較