📖

『SQLの苦手を克服する本』を読んでみて

に公開

1. はじめに

  • 『SQLの苦手を克服する本』を読了。
  • 読むきっかけは、PJ現場で初歩的なアンチパターンに引っ掛かることがあり、SQLの本質部分を事例ベースで学べる書籍を探していた。
  • 知識の定着のため、自分が気になった点をピックアップしてまとめる。

2. 本書の前提

  • SQLは集合指向の言語であり、手続き型言語とは全く違う概念であること

この前提を踏まえた上で、個人的に刺さったポイントについて書いていきます。

① 表形式のデータ操作イメージを持つ
② 「結合条件」と「抽出条件」の違い
③ CASE式とパラメータテーブルの活用
④ Entity-Attribute-Value手法はやめよう
⑤ インジェクション対策のためにもSQL動的組み立てはやめよう
⑥ SQLのための仕様書は書くだけ無駄

3. 個人的に刺さったポイント

① 表形式のデータ操作イメージを持つ

SQLを学ぶ上で「表形式のデータ操作イメージ」を持つと、直感的に理解しやすくなり、クエリの構造や処理の流れが頭に入りやすくなります。たとえば以下のような図解があると、SQLの処理がイメージしやすくなります。

■SELECT(列を選ぶ)

元のテーブル:

name age gender
Alice 25 female
Bob 35 male
Emma 28 female

クエリ:

SELECT name, age FROM users;

結果:

name age
Alice 25
Bob 35
Emma 28
■WHERE(行を選ぶ)

元のテーブル:

name age
Alice 25
Bob 35
Emma 28

クエリ:

SELECT * FROM users WHERE age > 30;

結果:

name age
Bob 35
■JOIN(表同士の結合)

usersテーブル:

id name
1 Alice
2 Bob

ordersテーブル:

user_id item
1 Laptop
2 Smartphone

クエリ:

SELECT * FROM users JOIN orders ON users.id = orders.user_id;

結果:

id name user_id item
1 Alice 1 Laptop
2 Bob 2 Smartphone
■GROUP BY(集計)

元のテーブル:

gender
male
female
male
female
male

クエリ:

SELECT gender, COUNT(*) FROM users GROUP BY gender;

結果:

gender COUNT(*)
male 3
female 2

② 「結合条件」と「抽出条件」の違い

書き方 特徴 落とし穴
WHERE に条件を書く 結合後のフィルタ 欲しい行が消える危険あり
JOIN ... ON に条件を書く 結合時に制御できる 欠落防止・意図が明確

③ CASE式とパラメータテーブルの活用

SQL内で条件分岐を行いたい場合、アプリケーション側でif文やfor文を書くのではなく、CASE式を使うことで「集合単位」での処理が可能になります。特に「行持ちデータを列持ちに変換(クロス集計)」する際に強力な武器になります。

また、条件値をクエリ内にハードコーディングせず、「パラメータテーブル(区分値管理テーブル)」に切り出す設計の重要性も本書では語られていました。

■CASE式の活用例(クロス集計)

元のテーブル(monthly_sales):

year month amount
2024 1 100
2024 2 150
2024 3 200

クエリ(月ごとの売上を横持ちに変換):

SELECT
    year,
    SUM(CASE WHEN month = 1 THEN amount ELSE 0 END) AS jan_sales,
    SUM(CASE WHEN month = 2 THEN amount ELSE 0 END) AS feb_sales,
    SUM(CASE WHEN month = 3 THEN amount ELSE 0 END) AS mar_sales
FROM monthly_sales
GROUP BY year;

④ Entity-Attribute-Value手法はやめよう

「将来どんなデータ項目が増えても対応できるように」という理由で、EAV(Entity-Attribute-Value)パターンを採用するのは避けるべきです。これは**「なんでも入る箱」を作ってしまうことによる弊害**がメリットを上回るためです。

■EAV(非推奨)のイメージ
id entity_id attribute value
1 101 name Alice
2 101 age 25
3 101 city Tokyo
■何がダメなのか
  • 型安全性が失われる: value カラムは通常VARCHAR型になりがちで、数値や日付としての整合性がDB側で担保できない。
  • クエリが複雑化する: 「名前」と「年齢」を1行で取得したい場合、何度もテーブルを自己結合(Self Join)する必要があり、可読性もパフォーマンスも悪化する。
  • 制約が使えない: NOT NULL 制約などが属性ごとに設定できないため、データ品質が低下する。

正規化されたテーブル設計を行うか、どうしてもスキーマレスな柔軟性が必要な場合は、JSON型の利用やNoSQLの検討が推奨されます。

⑤ インジェクション対策のためにもSQL動的組み立てはやめよう

文字列連結でSQLを組み立てるのは、セキュリティ(SQLインジェクション)の観点からも、パフォーマンス(実行計画のキャッシュ効率)の観点からもNGです。
必ず**プレースホルダ(バインド変数)**を使用する習慣をつける必要があります。

■悪い例(文字列連結)
# 悪意ある入力で意図しないデータ操作が可能になってしまう
sql = "SELECT * FROM users WHERE name = '" + user_input + "'"
■良い例(プレースホルダ)
# DBエンジン側で値を安全にエスケープ・処理してくれる
sql = "SELECT * FROM users WHERE name = ?"
params = [user_input]

また、プレースホルダを使うことで、DB側が「同じ構文のSQL」と認識しやすくなり、実行計画(Access Plan)の再利用率が上がり、パフォーマンス向上にも繋がります。

⑥ SQLのための仕様書は書くだけ無駄

「詳細設計書」として、Excel等で以下のようなドキュメントを作ることがありますが、これはメンテナンスコストの無駄であるという主張です。

仕様書例:
1. usersテーブルからageが20以上のデータを抽出する
2. ordersテーブルと結合する...

■なぜ無駄なのか
  • SQL自体が仕様書である: SQLは宣言型言語であり、「何が欲しいか(結果)」を記述しているため、コードそのものが仕様を表している。
  • 二重管理のコスト: SQLを修正したのに仕様書を直し忘れると、仕様書が嘘をつくことになる(そして誰も読まなくなる)。
■どうすべきか

「何をしているか(How)」を日本語訳するのではなく、**「なぜそのデータが必要なのか(Why/ビジネスロジック)」**をコメントやドキュメントに残すべきです。

4. 終わりに(感想)

  • 新しい発見というより、"強いエンジニア"たちの思考や習慣を学び、納得感が得られた。
  • 特に「理解に時間をかける」「伝えることをベースにする」は今後実践していきたい。

Discussion