0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SQL入門】データを手足のように操る技術 — SELECT から JOINまで完全攻略

0
Posted at

【SQL入門】データを手足のように操る技術 — SELECT から JOIN まで完全攻略

はじめに

データベースを使う上で避けて通れないのが SQL(Structured Query Language) です。

「なんとなく SELECT は書けるけど、JOIN になると怪しい…」
「WHERE と HAVING の違いがよくわからない…」

この記事では、そんな方に向けて 「データを自由自在に取り出し、加工し、操作する力」 を身につけてもらうことを目指します。

実際のデモデータを使いながら、手を動かして理解できる構成にしています。


この記事で使うデモデータ

まず、記事全体を通して使うテーブルを紹介します。
「社員管理システム」を想定した3テーブルです。

departments(部署)

id name
1 営業部
2 開発部
3 人事部

employees(社員)

id name department_id salary hire_date
1 田中 1 300000 2022-04-01
2 鈴木 2 450000 2021-06-15
3 佐藤 2 400000 2023-01-10
4 高橋 1 350000 2020-09-01
5 伊藤 3 320000 2023-07-01
6 山本 NULL 280000 2024-01-15

山本さんは配属先未定(NULL)です。NULLの扱いも後ほど解説します。

projects(プロジェクト)

id name leader_id
1 ECサイトリニューアル 2
2 社内ツール開発 3
3 採用管理システム 5

このデータを作成するSQLも載せておきます。手元で試したい方はどうぞ。

CREATE TABLE departments (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    salary INT,
    hire_date DATE
);

CREATE TABLE projects (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    leader_id INT
);

INSERT INTO departments VALUES
(1, '営業部'), (2, '開発部'), (3, '人事部');

INSERT INTO employees VALUES
(1, '田中', 1, 300000, '2022-04-01'),
(2, '鈴木', 2, 450000, '2021-06-15'),
(3, '佐藤', 2, 400000, '2023-01-10'),
(4, '高橋', 1, 350000, '2020-09-01'),
(5, '伊藤', 3, 320000, '2023-07-01'),
(6, '山本', NULL, 280000, '2024-01-15');

INSERT INTO projects VALUES
(1, 'ECサイトリニューアル', 2),
(2, '社内ツール開発', 3),
(3, '採用管理システム', 5);

1. SQL文の全体像 — 4つの操作(CRUD)

SQLの操作は大きく 4種類 に分かれます。

操作 SQL文 意味 頻度
Create INSERT データを追加する ★★☆
Read SELECT データを取得する ★★★
Update UPDATE データを更新する ★★☆
Delete DELETE データを削除する ★☆☆

現場で最も書く機会が多いのは圧倒的に SELECT です。
この記事でも SELECT を中心に、厚めに解説します。


2. SELECT — データを取り出す

基本の形

SELECT 列名 FROM テーブル名;

全列を取得するなら *(アスタリスク)を使います。

SELECT * FROM employees;

結果:

id name department_id salary hire_date
1 田中 1 300000 2022-04-01
2 鈴木 2 450000 2021-06-15
... ... ... ... ...

実務のコツ: SELECT * は楽ですが、本番コードでは必要な列だけ指定しましょう。不要な列まで取得するとパフォーマンスが落ちます。

特定の列だけ取りたいなら、カンマ区切りで列名を指定します。

SELECT name, salary FROM employees;
name salary
田中 300000
鈴木 450000
佐藤 400000
高橋 350000
伊藤 320000
山本 280000

WHERE — 条件で絞り込む

SELECT * FROM employees WHERE salary >= 350000;
id name department_id salary hire_date
2 鈴木 2 450000 2021-06-15
3 佐藤 2 400000 2023-01-10
4 高橋 1 350000 2020-09-01

比較演算子まとめ

演算子 意味
= 等しい WHERE name = '田中'
!= / <> 等しくない WHERE id != 1
> より大きい WHERE salary > 300000
>= 以上 WHERE salary >= 300000
< より小さい WHERE salary < 400000
<= 以下 WHERE salary <= 400000

AND / OR — 複数条件

-- 開発部かつ給与40万以上
SELECT * FROM employees
WHERE department_id = 2 AND salary >= 400000;
id name department_id salary hire_date
2 鈴木 2 450000 2021-06-15
3 佐藤 2 400000 2023-01-10
-- 営業部または人事部
SELECT * FROM employees
WHERE department_id = 1 OR department_id = 3;

注意: AND と OR を混ぜるときは 括弧 で意図を明確にしましょう。
WHERE a = 1 OR b = 2 AND c = 3WHERE a = 1 OR (b = 2 AND c = 3) と解釈されます(ANDが先)。

IN — 複数の値のどれかに一致

OR を何個も並べるのは面倒なので、IN を使います。

-- 上のORと同じ意味
SELECT * FROM employees
WHERE department_id IN (1, 3);

BETWEEN — 範囲指定

SELECT * FROM employees
WHERE salary BETWEEN 300000 AND 400000;

これは salary >= 300000 AND salary <= 400000 と同じ意味です。両端を含みます。

LIKE — あいまい検索

パターン 意味
% 0文字以上の任意の文字列 '田%' → 田中、田村、田…
_ ちょうど1文字 '_藤' → 佐藤、伊藤
SELECT * FROM employees WHERE name LIKE '%藤';
id name department_id salary hire_date
3 佐藤 2 400000 2023-01-10
5 伊藤 3 320000 2023-07-01

IS NULL / IS NOT NULL — NULLの判定

NULLは = で比較できません。 これは初心者がハマる最大のポイントです。

-- ❌ これは動かない(結果が返らない)
SELECT * FROM employees WHERE department_id = NULL;

-- ✅ 正しい書き方
SELECT * FROM employees WHERE department_id IS NULL;
id name department_id salary hire_date
6 山本 NULL 280000 2024-01-15

なぜ = NULL はダメなのか?
SQLの世界では NULL は「値が存在しない」という意味です。「存在しないもの」と何かを比較しても、結果は TRUE でも FALSE でもなく UNKNOWN になります。だから専用の IS NULL を使います。


ORDER BY — 並び替え

-- 給与の高い順(降順)
SELECT name, salary FROM employees
ORDER BY salary DESC;
name salary
鈴木 450000
佐藤 400000
高橋 350000
伊藤 320000
田中 300000
山本 280000
キーワード 意味
ASC 昇順(小さい順)— デフォルト
DESC 降順(大きい順)

複数列での並び替えもできます。

-- 部署ごとに、給与の高い順
SELECT * FROM employees
ORDER BY department_id ASC, salary DESC;

LIMIT — 取得件数を制限する

-- 上位3件だけ
SELECT name, salary FROM employees
ORDER BY salary DESC
LIMIT 3;
name salary
鈴木 450000
佐藤 400000
高橋 350000

DB製品による違い: MySQL・PostgreSQL・SQLite は LIMIT、Oracle は FETCH FIRST 3 ROWS ONLY、SQL Server は TOP 3 を使います。


DISTINCT — 重複を排除する

SELECT DISTINCT department_id FROM employees;
department_id
1
2
3
NULL

3. 集約関数とGROUP BY — データをまとめる

主な集約関数

関数 意味
COUNT(*) 行数を数える 全社員数
COUNT(列) NULLを除いた行数 部署が決まっている社員数
SUM(列) 合計 給与の合計
AVG(列) 平均 平均給与
MAX(列) 最大値 最高給与
MIN(列) 最小値 最低給与
SELECT
    COUNT(*) AS 全社員数,
    AVG(salary) AS 平均給与,
    MAX(salary) AS 最高給与,
    MIN(salary) AS 最低給与
FROM employees;
全社員数 平均給与 最高給与 最低給与
6 350000 450000 280000

AS は列に別名(エイリアス)をつけます。結果が読みやすくなるので積極的に使いましょう。

GROUP BY — グループごとに集計

SELECT
    department_id,
    COUNT(*) AS 人数,
    AVG(salary) AS 平均給与
FROM employees
GROUP BY department_id;
department_id 人数 平均給与
1 2 325000
2 2 425000
3 1 320000
NULL 1 280000

HAVING — グループに対する条件

WHERE は「各行」に対する条件、HAVING は「グループ」に対する条件です。

-- 平均給与が35万以上の部署だけ
SELECT
    department_id,
    AVG(salary) AS 平均給与
FROM employees
GROUP BY department_id
HAVING AVG(salary) >= 350000;
department_id 平均給与
2 425000

WHERE vs HAVING 早見表

WHERE HAVING
対象 各行 グループ
使える場所 GROUP BY の前 GROUP BY の後
集約関数 使えない ❌ 使える ✅
-- ❌ エラーになる
SELECT department_id, AVG(salary)
FROM employees
WHERE AVG(salary) >= 350000  -- 集約関数はWHEREで使えない
GROUP BY department_id;

-- ✅ 正しい
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id
HAVING AVG(salary) >= 350000;

4. SELECT文の実行順序 — 最重要ポイント

SQLは 書く順番実行される順番 が違います。これを知らないとエラーの原因がわからなくなります。

実行順 何をしているか
1 FROM テーブルを決める
2 WHERE 行を絞り込む
3 GROUP BY グループ化する
4 HAVING グループを絞り込む
5 SELECT 列を選ぶ・計算する
6 ORDER BY 並び替える
7 LIMIT 件数を制限する

これが分かると解ける疑問:

  • WHEREで集約関数が使えないのはなぜ? → WHEREはGROUP BYのに実行されるから、まだグループが存在しない
  • SELECTでつけた別名がWHEREで使えないのはなぜ? → SELECTはWHEREのに実行されるから
  • ORDER BYでは別名が使えるのはなぜ? → ORDER BYはSELECTのに実行されるから

5. JOIN — テーブルを結合する

ここからが本番です。実務では複数のテーブルを組み合わせてデータを取り出すケースがほとんどです。

INNER JOIN — 一致するデータだけ結合

SELECT
    e.name AS 社員名,
    d.name AS 部署名,
    e.salary
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;
社員名 部署名 salary
田中 営業部 300000
鈴木 開発部 450000
佐藤 開発部 400000
高橋 営業部 350000
伊藤 人事部 320000

山本さん(department_id = NULL)は結果に含まれません。一致する部署がないからです。

edテーブルの別名(エイリアス) です。毎回 employees.name と書くのは長いので、短い別名をつけます。

LEFT JOIN — 左テーブルを全て残す

SELECT
    e.name AS 社員名,
    d.name AS 部署名
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
社員名 部署名
田中 営業部
鈴木 開発部
佐藤 開発部
高橋 営業部
伊藤 人事部
山本 NULL

山本さんも結果に含まれるようになりました。一致する部署がない場合、部署名は NULL になります。

JOIN の種類まとめ

INNER JOIN        LEFT JOIN         RIGHT JOIN        FULL OUTER JOIN
┌───┬───┐        ┌───┬───┐        ┌───┬───┐        ┌───┬───┐
│ A │ B │        │ A │ B │        │ A │ B │        │ A │ B │
│   │███│        │███│███│        │███│███│        │███│███│
│   │███│        │███│███│        │███│███│        │███│███│
│   │   │        │███│   │        │   │███│        │███│███│
└───┴───┘        └───┴───┘        └───┴───┘        └───┴───┘
 両方に一致        左を全て残す      右を全て残す       両方全て残す
種類 説明 使用頻度
INNER JOIN 両テーブルに一致するデータのみ ★★★
LEFT JOIN 左テーブルは全て残す。右に一致がなければNULL ★★★
RIGHT JOIN 右テーブルは全て残す ★☆☆
FULL OUTER JOIN 両方全て残す ★☆☆

実務のコツ: RIGHT JOIN はテーブルの順番を入れ替えれば LEFT JOIN で書けるので、ほぼ使いません。LEFT JOIN だけ覚えておけばOKです。

3テーブル以上の結合

JOINは何個でもつなげられます。

-- 社員 + 部署 + 担当プロジェクト
SELECT
    e.name AS 社員名,
    d.name AS 部署名,
    p.name AS プロジェクト名
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
LEFT JOIN projects p ON e.id = p.leader_id;
社員名 部署名 プロジェクト名
田中 営業部 NULL
鈴木 開発部 ECサイトリニューアル
佐藤 開発部 社内ツール開発
高橋 営業部 NULL
伊藤 人事部 採用管理システム
山本 NULL NULL

6. サブクエリ — SQLの中にSQLを書く

クエリの結果を別のクエリの条件に使えます。

-- 平均給与より高い社員を取得
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
name salary
鈴木 450000
佐藤 400000
-- プロジェクトリーダーの一覧
SELECT name FROM employees
WHERE id IN (SELECT leader_id FROM projects);
name
鈴木
佐藤
伊藤

実務のコツ: サブクエリで書けるものは JOIN でも書けることが多いです。パフォーマンス面では JOIN の方が有利なケースが多いので、複雑になったら JOIN への書き換えを検討しましょう。


7. INSERT / UPDATE / DELETE — データの変更

INSERT — 追加

-- 1件追加
INSERT INTO employees (id, name, department_id, salary, hire_date)
VALUES (7, '渡辺', 2, 380000, '2024-04-01');

-- 複数件を一括追加
INSERT INTO employees (id, name, department_id, salary, hire_date)
VALUES
    (8, '中村', 1, 310000, '2024-04-01'),
    (9, '小林', 3, 290000, '2024-04-01');

UPDATE — 更新

-- 田中の給与を上げる
UPDATE employees
SET salary = 330000
WHERE id = 1;

⚠️ WHERE を忘れると全行が更新されます。 これは取り返しのつかない事故になるので、UPDATE/DELETEを実行する前に、まず同じ WHERE 条件で SELECT して対象行を確認する癖をつけましょう。

-- 先にこれで確認
SELECT * FROM employees WHERE id = 1;

-- 確認できたら UPDATE
UPDATE employees SET salary = 330000 WHERE id = 1;

DELETE — 削除

DELETE FROM employees WHERE id = 9;

WHERE を忘れると 全データが消えます。UPDATE と同じく、先に SELECT で確認しましょう。


8. 実践:よくある実務クエリ集

ここまでの知識を組み合わせた、実務でよく見るパターンです。

部署ごとの人数と平均給与(部署名付き)

SELECT
    d.name AS 部署名,
    COUNT(e.id) AS 人数,
    AVG(e.salary) AS 平均給与
FROM departments d
LEFT JOIN employees e ON d.id = e.department_id
GROUP BY d.id, d.name
ORDER BY 平均給与 DESC;
部署名 人数 平均給与
開発部 2 425000
営業部 2 325000
人事部 1 320000

各部署で最も給与が高い社員

SELECT e.name, d.name AS 部署名, e.salary
FROM employees e
INNER JOIN departments d ON e.department_id = d.id
WHERE e.salary = (
    SELECT MAX(salary)
    FROM employees
    WHERE department_id = e.department_id
);
name 部署名 salary
高橋 営業部 350000
鈴木 開発部 450000
伊藤 人事部 320000

プロジェクトを持っていない社員

SELECT e.name
FROM employees e
LEFT JOIN projects p ON e.id = p.leader_id
WHERE p.id IS NULL;
name
田中
高橋
山本

9. チートシート

最後に、この記事の内容を1枚にまとめます。

【SELECT文の構文】
SELECT  列名           -- 5番目に実行
FROM    テーブル        -- 1番目
JOIN    テーブル ON 条件 -- 1番目(FROMと一緒)
WHERE   行の条件        -- 2番目
GROUP BY グループ化列   -- 3番目
HAVING  グループの条件  -- 4番目
ORDER BY 並び替え       -- 6番目
LIMIT   件数制限        -- 7番目

【CRUD】
INSERT INTO t (列) VALUES (値);
UPDATE t SET 列 = 値 WHERE 条件;  ← WHERE忘れ厳禁
DELETE FROM t WHERE 条件;          ← WHERE忘れ厳禁

【JOIN】
INNER JOIN  → 両方一致のみ
LEFT JOIN   → 左を全て残す(これだけ覚えればOK)

【NULL】
= NULL は使えない → IS NULL / IS NOT NULL

おわりに

SQLは覚えることが多く見えますが、核になるのは SELECT + WHERE + JOIN + GROUP BY の4つです。この4つを自在に組み合わせられれば、大抵のデータは取り出せます。

まずはデモデータを自分のDBに入れて、この記事のクエリを実際に動かしてみてください。手を動かした分だけ、SQLは身体に染み込んでいきます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?