はじめに
ゆくゆくはWebアプリケーションを作成したいと考えているので、今回はその上で必要になるデータベースの設計とSQL文について「スッキリわかるSQL入門」という書籍で学んだのでアプトプットも兼ねて書評をしていきます〜🐳
良かったところ
個人的にはかなりの良書だと感じました
- 情報がただ羅列されているだけではなく、困ったことや詰まったという背景をもとに解決策としてコードなどが図解で紹介されているので、イメージが湧いて非常に分かりやすかった
- 左脳と右脳両方から学べる
- 優しい言葉で書かれているため、語句などを追加で調べる必要がなく学習コストが低い
- 裏で行われているデータ処理まで説明してくれるので根本から理解できる
自分はこれまでデータベースなどをあまり触ったことがなかったので、学習のとっかかりとしてはかなり良かったです
悪かったところ
特にないですが、強いて言えば演習問題の解答の位置と解答に解説がないことで学習効率が落ちるところです
- 演習問題の答えの位置が巻末にあることでわざわざ巻末のページを探す時間が増えること
- 解答に解説がないことでわからなかった箇所は過去のページを振り返って該当箇所を探す時間が増えること
作成の都合上、
- ページが増えすぎると重くなり持ちづらくなる
- 字が小さくなることで難しく感じ、学習コストが上がる
- あえて分からない箇所を自分で探す手間を設けることで定着率を上げる
もちろん他の要因もあるかもしれませんが、見開きで左に演習、右に解答を配置し、解答には解説を添えて復習できるようになっていると、よりありがたいと感じました
学んだこと
SQL4大命令文と分類
1. SQL4大命令文
- SELECT … データの検索(取り出し)
- INSERT … データの追加
- UPDATE … データの更新
- DELETE … データの削除
この4つをまとめて「SQLの4大命令」と呼ぶ(DML:Data Manipulation Language)
2. 検索系と更新系
-
検索系
- SELECT(既存データを検索)
-
更新系
- INSERT(新規データを追加)
- UPDATE(既存データを更新)
- DELETE(既存データを削除)
3. 既存系と新規系
-
既存系
- SELECT(既存データを検索)
- UPDATE(既存データを更新)
- DELETE(既存データを削除)
-
新規系
- INSERT(新しいデータを追加)
IN / NOT IN 演算子
IN 演算子
- 指定した複数の値の中に対象が「含まれる」かを判定する
- 例
SELECT name
FROM users
WHERE age IN (20, 25, 30);
- 効用: OR条件を簡潔に書ける
- 例
age = 20 OR age = 25 OR age = 30
⇔ age IN (20,25,30)
)
NOT IN 演算子
- 指定した複数の値の中に対象が「含まれない」かを判定する
- 例
SELECT name
FROM users
WHERE age NOT IN (20, 25, 30);
注意点
-
NOT IN
の候補にNULL
が含まれると、結果が返らなくなる場合がある
ANY / ALL 演算子
ANY 演算子
- 条件が「いずれか1つ以上」に当てはまれば真になる
- よく使う形:
= ANY
,< ANY
,> ANY
など - 例
SELECT name, age
FROM users
WHERE age = ANY (20, 30, 40);
ALL 演算子
- 条件が「すべて」に当てはまるときだけ真になる
- よく使う形:
= ALL
,< ALL
,> ALL
など - 例
SELECT name, age
FROM users
WHERE age > ALL (20, 25, 30);
特徴
-
ANY
は OR 条件の簡潔表現 -
ALL
は「すべてに一致」や「最大値・最小値チェック」に使える
IN / NOT IN と ANY / ALL の使い分け
IN / NOT IN を使うタイミング
- 特定の値の集合に一致するかどうかをシンプルに書きたいとき
- 例: 年齢が 20, 25, 30 のいずれか →
age IN (20,25,30)
- 主に
=
比較に限定して使う場合に便利
ANY / ALL を使うタイミング
- 大小比較を含めて複数の値と比較したいとき
- 例: 年齢が「候補の中で一番大きい値より大きい」など
- 最大値・最小値チェックをシンプルに書きたいときに有効
まとめ
- 値の「一致/不一致」だけなら IN / NOT IN
- 値の「大小比較」まで含めたいなら ANY / ALL
論理演算子の優先度
優先度の基本
- NOT
- AND
- OR
同じ式の中ではこの順番で評価される
例
SELECT *
FROM users
WHERE NOT age = 20 OR age = 30 AND gender = 'M';
実際の解釈
WHERE (NOT age = 20) OR (age = 30 AND gender = 'M');
() で優先度を変更
演算子の優先度は () で引き上げ可能。複雑な条件では可読性のためにも明示的に括弧を付ける。
WHERE (NOT (age = 20 OR age = 30)) AND gender = 'M';
OFFSET - FETCH の使い方
基本構文
SELECT 列名
FROM テーブル名
ORDER BY 並び替え条件
OFFSET 開始位置 ROWS
FETCH NEXT 取得件数 ROWS ONLY;
-
OFFSET
スキップする行数を指定する -
FETCH NEXT
取得する行数を指定する
例1: 先頭から10件目以降を5件取得
SELECT *
FROM users
ORDER BY id
OFFSET 10 ROWS
FETCH NEXT 5 ROWS ONLY;
id順で並べた結果の「11件目〜15件目」を取得する
例2: ページング処理に応用
- 1ページ10件表示のとき
- 1ページ目:
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
- 2ページ目:
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
- 3ページ目:
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
- 1ページ目:
注意点
- ORDER BY が必須(順序が決まらないと OFFSET の意味がなくなる)
- 大量データではパフォーマンスに影響することがある
BETWEEN の使い方
基本
BETWEEN
は「両端を含む」
WHERE 日付 BETWEEN '2025-09-01' AND '2025-09-10';
9/1 も 9/10 も含まれる
含まない場合
BETWEEN は必ず両端を含むため、含めたくない場合は > や < を明示する
WHERE 日付 > '2025-09-01' AND 日付 < '2025-09-10';
9/1 と 9/10 は含まない
まとめ
- 端も含めたい → BETWEEN
- 端を含めたくない/柔軟に条件指定したい → >=, <=, >, <
GROUP BY の使い方と WHERE・HAVING の違い
GROUP BY とは
- 同じ値を持つ行をまとめ、グループごとに集計するための構文
-
COUNT
・SUM
・AVG
・MAX
・MIN
などの集計関数と一緒に使う
-- 部署ごとの社員数を数える
SELECT department, COUNT(*) AS num_employees
FROM employees
GROUP BY department;
WHERE と HAVING の違い
WHERE
- グループ化の前に行を絞り込む
- 行(レコード)単位で条件を指定する
- WHEREに集計関数を入れることはできない
- WHEREを実施時点ではまだ集計関数が実施されていないから値がないため
グループ集計の流れ
- 1:検索
- WHRER句によって行を絞り込む
- 2:グループ化
- グループごとに検索結果を分類する
- 3:集計・列選択
- 各グループを集計する
- SELECT句によって列を絞り込む
-- 給与30万円以上の社員だけを対象に集計
SELECT department, COUNT(*) AS num_employees
FROM employees
WHERE salary >= 300000
GROUP BY department;
HAVING
- グループ化の後に条件をかける
- グループ単位で条件を指定する
グループ集計の流れ(HAVING有)
- 1:検索
- WHRER句によって行を絞り込む
- 2:グループ化
- グループごとに検索結果を分類する
- 3:集計・列選択
- 各グループを集計する
- SELECT句によって列を絞り込む
- 4:集計結果の絞り込み
- HAVING句は集計後に絞り込み
よって、集計関数を絞り込みで使いたい場合はHAVINGを使う
-- 部署ごとの人数を数え、5人以上いる部署だけを表示
SELECT department, COUNT(*) AS num_employees
FROM employees
GROUP BY department
HAVING COUNT(*) >= 5;
WHERE と HAVING を両方使う例
-
WHERE
でまず対象行を減らし、HAVING
で集計結果に条件をかける
SELECT department, COUNT(*) AS num_employees, AVG(salary) AS avg_salary
FROM employees
WHERE salary >= 300000 -- 行レベルの条件
GROUP BY department
HAVING COUNT(*) >= 5 -- グループレベルの条件
ORDER BY avg_salary DESC;
使い分け
-
WHERE → グループ化前に「行」を絞る
(例: 給与30万以上の社員だけ対象) -
HAVING → グループ化後に「グループ」を絞る
(例: 社員数が5人以上の部署だけ残す)
データの正規化とパフォーマンスのトレードオフ
正規化とは
- データの冗長性(重複)をなくし、整合性を保つ設計手法
- 更新・削除・追加のときに不整合が起きにくい
- ストレージも効率的に使える
users(id, name)
products(id, name, price)
orders(id, user_id, product_id)
正規化のメリット・デメリット
- メリット:整合性が高い、データ品質が良い
- デメリット:テーブル分割が進み、JOINが増えるので検索・集計が遅くなる
非正規化とは
- 正規化をあえて崩して、データを重複させる設計
- 検索や集計を高速化する目的で行う
例
orders(id, user_name, product_name, product_price)
非正規化のメリット・デメリット
- メリット:JOINなしで取り出せるのでパフォーマンスが向上する
- デメリット:同じデータが複数箇所に存在し、不整合のリスクが高くなる
まとめ
- 正規化をすると整合性はUPするがパフォーマンスDOWNする
- 非正規化はパフォーマンスはUPするが整合性がDOWNする
- システム特性に合わせてバランスを取ることが重要である
- 更新の正確さが最重要 → 正規化
- 読み取りが多い → 非正規化やキャッシュ活用
1対多のイメージ
- ツリー構造
Department
├─ Student
├─ Student
└─ Student
※ ERでの1件に対してN件が紐づく関係
1つの学部に対して複数の生徒が属しているが
人の生徒は1つの学部にしか属していない(1対多である)
難しかったところ
下記2点です
-
コードや考え自体に難しいところはありませんでしたが、自分がやりたい処理をどのコードで実施するのかを判断するのは、SQL文をたくさん打たないと身につかないというところだと思うので大変そう
-
DBMSによって仕様が異なるところ
さいごに
今回の書籍は、自分のようにこれからDBについて学ぼうと思っている人には良書だと感じました
- 学習に取りかかりやすいように書かれている順番の内容も正規化などの概念的なところからではなく、実用に近いSELECT文から書かれている
- 優しい言葉が使われている
- 図解で使うタイミングがイメージしやすい
- コードの型→実用例の順番で書かれており整理されている
逆にある程度DBを使っている人からしたら物足りないと内容だと思います
1周読んだだけなので、隙間時間を見つけて演習問題などを取り組んで定着させていきます!