SQL

Tech
Table of Contents

図1: 学習全体の地図

順番 何を学ぶか 到達目標
1 データベース DBMS、リレーショナルモデル、データモデル SQLが前提にしている世界観を理解する
2 SQL I DDL、SELECT、集約、JOIN 基本的なデータ取得とテーブル定義ができる
3 トランザクション ACID、分離性、分離レベル 複数処理を安全に扱う考え方を理解する
4 データベース設計 ERモデル、論理設計、正規化 要件をテーブル構造へ落とし込める
5 インデックス B+Tree、カーディナリティ、カバリングインデックス 検索高速化の基本を理解する
6 実行計画 クエリ評価、EXPLAIN、統計情報 遅いSQLを調査する入口を知る
7 SQL II サブクエリ、CASE式 SQLで条件分岐や複雑な問い合わせを書ける

1. データベースの基礎

データベースは、実世界のある側面をモデル化した、秩序ある相互に関連したデータの集合です。DBMSは、アプリケーションがデータを保存・検索・更新しやすくするためのソフトウェアです。

初期のDBMSでは、アプリケーションが物理ファイルの構造を強く意識しなければならず、ファイル形式が変わるとアプリケーション側も変更が必要でした。この問題を解消するために、論理的なデータ表現と物理的な保存形式を分離する考え方が重要になります。

リレーショナルモデルは、データをリレーションとして表現し、高水準な言語で操作するモデルです。SQLはその代表的な操作言語です。


図2: 論理層と物理層の分離

役割
アプリケーション 業務処理を実行する 学生名簿アプリ、購買システム
論理層 テーブルや列としてデータを見る 学生、部活、所属
DBMS 論理操作を物理アクセスへ変換する MySQL、Oracle、SQLite
物理層 実際の保存形式 ファイル、ページ、インデックス

ポイントは、アプリケーションが物理ファイルを直接扱わず、テーブルという抽象化を通してデータにアクセスできることです。


2. SQL I: テーブル定義と基本問い合わせ

SQLには複数のサブ言語があります。

分類 役割 代表例
DDL データ構造を定義する CREATE TABLE, ALTER TABLE, DROP TABLE
DML データを操作する SELECT, INSERT, UPDATE, DELETE
DCL 権限を制御する GRANT, REVOKE
TCL トランザクションを制御する BEGIN, COMMIT, ROLLBACK

テーブル定義の基本

CREATE TABLE student (
  sid INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  age INTEGER,
  major TEXT
);

主キーは行を一意に識別する列です。外部キーは、別テーブルの主キーを参照し、テーブル間の整合性を保つために使います。

CREATE TABLE club_member (
  sid INTEGER,
  cid INTEGER,
  joined_at DATE,
  PRIMARY KEY (sid, cid),
  FOREIGN KEY (sid) REFERENCES student(sid),
  FOREIGN KEY (cid) REFERENCES club(cid)
);

基本的に主キーと外部キーは設定しておくべきです。これにより、重複や参照先のないデータを防ぎやすくなります。


SELECT文の基本

SELECT はデータを取得するための文です。

SELECT sid, name, age
FROM student
WHERE major = '計算機科学'
ORDER BY age DESC
LIMIT 10;

よく使う句は次の通りです。

役割
SELECT 出力する列や式を指定する
FROM 対象テーブルを指定する
WHERE 行を絞り込む
GROUP BY グループ化する
HAVING グループ化後に絞り込む
ORDER BY 並び替える
LIMIT 返す行数を制限する

図3: SELECT文の論理的な評価順序

順番 処理
1 FROM どのテーブルを読むか
2 JOIN どのテーブルを結合するか
3 WHERE どの行を残すか
4 GROUP BY どの単位で集約するか
5 HAVING 集約後にどのグループを残すか
6 SELECT 何を出力するか
7 ORDER BY どう並べるか
8 LIMIT 何行返すか

SQLは書く順番と評価される順番が異なります。特に、WHERE は集約前、HAVING は集約後に効く点が重要です。


集約とGROUP BY

集約関数は複数行の値から1つの値を返します。

SELECT COUNT(*)
FROM student
WHERE major = '計算機科学';
SELECT major, AVG(age), COUNT(*)
FROM student
GROUP BY major;

GROUP BY を使うと、個々の行ではなくグループ単位の属性を扱います。そのため、集約されていない列を不用意に SELECT に出すと、意味が曖昧になります。


JOIN

JOINは複数のテーブルを組み合わせて問い合わせる仕組みです。

JOIN 意味
CROSS JOIN 直積。すべての組み合わせを作る
INNER JOIN 条件に一致する行だけ残す
LEFT OUTER JOIN 左テーブルの行を残し、一致しない右側はNULLにする
SELECT s.name, c.name AS club_name
FROM student s
INNER JOIN club_member cm ON s.sid = cm.sid
INNER JOIN club c ON cm.cid = c.cid;

JOINを書くときは、どの列同士が同じ意味を持つのかを明確にすることが重要です。


3. トランザクション

トランザクションは、複数の読み書きを論理的な1つの単位としてまとめる仕組みです。代表例は口座振込です。

BEGIN;

UPDATE account
SET balance = balance - 10000
WHERE account_id = 'A';

UPDATE account
SET balance = balance + 10000
WHERE account_id = 'B';

COMMIT;

途中で失敗した場合は ROLLBACK します。トランザクションは、全体として成功するか、全体として失敗するかのどちらかです。


図4: ACID特性

特性 意味 学習上のポイント
Atomicity 原子性 全部成功するか、全部なかったことにする
Consistency 一貫性 処理の前後でデータの制約が壊れない
Isolation 分離性 並行実行中のトランザクションが互いに悪影響を与えない
Durability 永続性 コミットした変更は障害後も残る

一貫性はDBMSだけでなく、アプリケーション側の設計にも関わります。DBMSが提供する制約だけでなく、業務ルールをどう守るかも重要です。


分離性と読み取り異常

並行実行では、他のトランザクションの影響で読み取り結果が変わることがあります。

現象 内容
ダーティリード コミットされていない変更を読んでしまう
ファジーリード 同じ行を読み直したら値が変わる
ファントムリード 同じ条件で検索したのに行が増減する

分離レベルを上げるほど安全になりますが、並行性や性能とのトレードオフがあります。


4. データベース設計

データベース設計は、要求分析、概念設計、論理設計、物理設計の流れで進みます。

段階 目的
要求分析 管理したいデータや使われ方を整理する
概念設計 ERモデルなどで業務世界を表現する
論理設計 テーブル、主キー、外部キーへ変換する
物理設計 インデックスや保存方式を決める

ERモデル

ERモデルでは、実体、属性、関連を使って対象世界を表します。

概念 意味
実体 管理したい対象 学生、部活、科目
属性 実体が持つ性質 氏名、年齢、部活名
関連 実体間の関係 学生は部活に所属する
カーディナリティ 関係の数の制約 1対多、多対多
参加制約 必ず参加するか 各部活には部長が必要

多対多の関連は、そのままテーブルにしにくいため、中間テーブルへ変換します。

CREATE TABLE club_member (
  sid INTEGER,
  cid INTEGER,
  joined_at DATE,
  PRIMARY KEY (sid, cid)
);

正規化

正規化の目的は、リレーションから冗長性を排除し、更新時異常を起こりにくくすることです。

正規形 目的
1NF 各属性が分解不能な値を持つ
2NF 主キーの一部だけに依存する非キー属性を排除する
3NF 非キー属性から非キー属性への依存を排除する
BCNF すべての決定項が候補キーになるようにする

正規化は万能ではありません。冗長性を減らす一方で、JOINが増える場合もあります。まずは不整合を防げる構造を作り、その後に性能要件を見て物理設計で調整します。


図5: 正規化の見方

見つけたい問題 典型的な依存 対応
同じ値が何度も出る キーの一部 → 非キー属性 2NFへ分解
ある列から別の列が決まる 非キー属性 → 非キー属性 3NFへ分解
キーでない列が決定項になる 非キー属性 → キーの一部 BCNFを検討

正規化は、データの意味と依存関係を見ながら進めます。単に表を細かく分ける作業ではありません。


5. インデックス

インデックスは検索を高速化するためのデータ構造です。RDBではB+Treeが代表的です。

ポイント 内容
等価検索 WHERE id = 10 のような検索に効きやすい
範囲検索 BETWEEN, <, > のような検索にも使える
カーディナリティ 値の種類が多い列ほど効きやすい傾向
更新コスト INSERT/UPDATE/DELETE時にインデックス更新が必要

インデックスは貼れば貼るほど速くなるものではありません。必要な検索条件に合わせて設計します。

CREATE INDEX idx_student_major ON student (major);

カバリングインデックス

検索条件と取得列がインデックスだけで足りると、テーブル本体を読まずに済む場合があります。

CREATE INDEX idx_club_member_covering
ON club_member (cid, joined_at);

SELECT joined_at
FROM club_member
WHERE cid = 10;

ただし、DBMSや実行計画によって使われるかは変わります。インデックスを作った後は、実行計画で確認します。


6. 実行計画

実行計画は、DBMSがSQLをどのような手順で処理するかを表したものです。MySQLでは EXPLAIN を使います。

EXPLAIN
SELECT s.name, s.age, c.name
FROM student s
INNER JOIN club_member cm ON s.sid = cm.sid
INNER JOIN club c ON cm.cid = c.cid;
項目 見ること
table アクセス対象テーブル
type アクセス方法
possible_keys 利用可能なインデックス
key 実際に選ばれたインデックス
key_len 読み取ったインデックス長
rows スキャン見積もり行数

実行計画は推定に基づくため、統計情報が古いと実態とずれることがあります。


図6: 遅いSQLを改善する流れ

ステップ やること
1 遅いSQLを特定する
2 EXPLAIN で実行計画を見る
3 フルスキャン、JOIN順序、利用インデックスを確認する
4 WHERE句、JOIN条件、SELECT列を見直す
5 必要ならインデックスを追加・変更する
6 再度 EXPLAIN と実測で確認する

SQLチューニングは、推測でインデックスを貼るのではなく、計画を見て仮説検証することが重要です。


7. SQL II: サブクエリとCASE式

サブクエリは、SQLの中に別のSQLを入れる書き方です。

SELECT s.name, s.major
FROM student s
WHERE s.sid IN (
  SELECT cm.sid
  FROM club_member cm
  WHERE cm.cid = 10
);

CASE式はSQL内で条件分岐を書くために使います。

SELECT
  major,
  SUM(CASE WHEN age >= 22 THEN 1 ELSE 0 END) AS over_22,
  SUM(CASE WHEN age >= 20 AND age < 22 THEN 1 ELSE 0 END) AS between_20_and_21
FROM student
GROUP BY major;

CASE式は集計と組み合わせると、条件別の件数集計に便利です。


学習時の重要ポイント

  1. SQLは書く順番ではなく、論理的な評価順序で理解する。
  2. JOINは「どの関係を復元しているのか」を意識する。
  3. トランザクションは、複数の処理を安全な1単位にする仕組み。
  4. 設計では、ERモデルで意味を整理し、正規化で冗長性を減らす。
  5. インデックスは検索を速くするが、更新コストも増える。
  6. 遅いSQLは、必ず実行計画を見てから改善する。
  7. サブクエリとCASE式は、複雑な問い合わせを表現するための道具。

最後に

この資料は、単なるSQL構文集ではなく、SQLを支えるデータベース理論と実務上の性能改善まで一通り扱っています。初学者はまず SELECT, JOIN, GROUP BY を手で書けるようにし、その後でトランザクション、設計、インデックス、実行計画へ進むと理解しやすくなります。

SQLは「データを取る文法」だけではなく、「データをどう表現し、どう守り、どう速く扱うか」を含む技術です。

コメント