メインコンテンツへスキップ

3.5.3 SQL 基礎

SQL 表の関連図

この節の位置づけ

SQL を初めて学ぶとき、新人が最もつまずきやすいのは、文法が多いことよりも次の2点です。

  • Pandas と一体どういう関係なのか
  • クエリの実行順を頭の中でどう整理するか

なので、この節でいちばん大事なのは、最初にすべての文法を暗記することではなく、まず次の判断を持つことです。

SQL の本質は、安定した言語で表に質問すること。

学習目標

  • SQL の4大操作を理解する:追加、削除、更新、検索
  • SELECT クエリを使いこなす
  • WHERE による条件フィルタを学ぶ
  • JOIN による複数テーブル結合を理解する
  • GROUP BY によるグループ化と集計を理解する

まず地図を1枚作ろう

SQL を新人が理解しやすい順番は、「最初から文法を丸暗記する」ではなく、まず全体像をつかむことです。

この節で本当に解決したいのは、次の2つです。

  • SQL のクエリは頭の中でどう流れるのか
  • なぜ Pandas の絞り込み、グループ化、結合に対応づけられるのか

SQL とは?

SQL(Structured Query Language、構造化問い合わせ言語)は、データベースと「対話」するための言語です。SQLite、MySQL、PostgreSQL のどれを使っても、SQL の基本文法はほぼ共通です。

SQL と Pandas の関係

SQL でできることの多くは、Pandas でもできます。実際、Pandas の多くのメソッド名(たとえば mergegroupby)は SQL からヒントを得ています。両方を対応させながら学ぶと、理解しやすくなります。

新人向けの、よりわかりやすい比喩

SQL は次のように考えるとよいです。

  • あなたがデータベースに質問している

その質問は、だいたいとても素朴です。

  • どの列がほしい?
  • どの行だけほしい?
  • 何で分ける?
  • 2つの表をどうつなぐ?

この比喩は新人にとても向いています。SQL を「別の言語」ではなく、「表にどう質問するか」として捉え直せるからです。


準備:練習用データベースを作る

この節の例はすべてこの練習用データベースに基づいています。先に実行してください。

import sqlite3

conn = sqlite3.connect(":memory:") # メモリ上のデータベース。閉じると消える
cursor = conn.cursor()

# users テーブルを作成
cursor.execute("""
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER,
city TEXT,
salary REAL
)
""")

# orders テーブルを作成
cursor.execute("""
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
user_id INTEGER,
product TEXT,
amount REAL,
order_date TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
)
""")

# ユーザーデータを挿入
users_data = [
(1, "张三", 28, "北京", 15000),
(2, "李四", 35, "上海", 22000),
(3, "王五", 22, "广州", 8000),
(4, "赵六", 42, "北京", 35000),
(5, "钱七", 30, "上海", 18000),
(6, "孙八", 26, "深圳", 12000),
]
cursor.executemany("INSERT INTO users VALUES (?, ?, ?, ?, ?)", users_data)

# 注文データを挿入
orders_data = [
(101, 1, "iPhone", 7999, "2024-11-01"),
(102, 1, "AirPods", 999, "2024-11-05"),
(103, 2, "MacBook", 14999, "2024-11-10"),
(104, 3, "iPad", 3999, "2024-11-15"),
(105, 2, "キーボード", 599, "2024-11-20"),
(106, 4, "モニター", 2999, "2024-12-01"),
(107, 5, "マウス", 299, "2024-12-05"),
]
cursor.executemany("INSERT INTO orders VALUES (?, ?, ?, ?, ?)", orders_data)

conn.commit()

# クエリを簡単に実行するための補助関数を定義
def query(sql):
cursor.execute(sql)
cols = [desc[0] for desc in cursor.description]
rows = cursor.fetchall()
# ヘッダーを表示
print(" | ".join(cols))
print("-" * (len(" | ".join(cols))))
for row in rows:
print(" | ".join(str(v) for v in row))
print()

一、データを検索する(SELECT)

SELECT は SQL の中で最もよく使う文で、テーブルからデータを取り出すために使います。

基本の検索

-- すべての列を検索
SELECT * FROM users;

-- 指定した列だけ検索
SELECT name, age, city FROM users;

-- 列に別名をつける
SELECT name AS 氏名, age AS 年齢 FROM users;
query("SELECT * FROM users")
# id | name | age | city | salary
# 1 | 张三 | 28 | 北京 | 15000
# 2 | 李四 | 35 | 上海 | 22000
# ...

DISTINCT:重複を除く

-- 重複しない都市名をすべて検索
SELECT DISTINCT city FROM users;

LIMIT:件数を制限する

-- 先頭3件だけ取得
SELECT * FROM users LIMIT 3;

最初にクエリを書くときの、いちばん安定した順番

基本的には、次の順番が書きやすいです。

  1. まず SELECT
  2. 次に FROM
  3. 必要なら WHERE
  4. 最後に並び替えやグループ化

最初から全部の句を一気に混ぜるより、ずっと整理しやすくなります。


二、条件で絞り込む(WHERE)

WHERE は Pandas のブールインデックスのようなもので、条件に合う行だけを選びます。

基本比較

-- 年齢が 30 より大きい
SELECT * FROM users WHERE age > 30;

-- 都市が北京
SELECT * FROM users WHERE city = '北京';

-- 給与が 10000 から 20000 の間
SELECT * FROM users WHERE salary BETWEEN 10000 AND 20000;

条件の組み合わせ

-- AND:両方満たす
SELECT * FROM users WHERE city = '北京' AND age > 25;

-- OR:どちらかを満たす
SELECT * FROM users WHERE city = '北京' OR city = '上海';

-- IN:リストの中にある
SELECT * FROM users WHERE city IN ('北京', '上海', '深圳');

-- NOT:否定する
SELECT * FROM users WHERE city NOT IN ('北京');

あいまい検索(LIKE

-- % は任意の文字列にマッチ(Pandas の str.contains に近い)
SELECT * FROM users WHERE name LIKE '张%'; -- 「张」で始まる
SELECT * FROM users WHERE name LIKE '%三'; -- 「三」で終わる
SELECT * FROM users WHERE email LIKE '%@mail%'; -- 「@mail」を含む

NULL の扱い

-- 空かどうかを判定する(= NULL は使えない)
SELECT * FROM users WHERE city IS NULL;
SELECT * FROM users WHERE city IS NOT NULL;

SQL と Pandas の対応表

目的SQLPandas
年齢が 30 より大きいWHERE age > 30df[df["age"] > 30]
都市が北京WHERE city = '北京'df[df["city"] == "北京"]
複数条件 ANDWHERE age > 30 AND city = '北京'df[(df["age"] > 30) & (df["city"] == "北京")]
複数条件 ORWHERE city IN ('北京', '上海')df[df["city"].isin(["北京", "上海"])]
あいまい検索WHERE name LIKE '张%'df[df["name"].str.startswith("张")]
空値WHERE city IS NULLdf[df["city"].isna()]

初学者が最初に覚えるとよい対応表

頭の中の質問近い SQL
条件に合う記録だけ見るWHERE
1つの表の中の重複しない値を見るDISTINCT
まず数件だけ確認するLIMIT
2つの表をつなぐJOIN
先にグループ分けしてから集計するGROUP BY

この表は新人にとても役立ちます。SQL をキーワードの一覧ではなく、よくある質問の種類として整理できるからです。


三、並び替え(ORDER BY)

-- 給与の昇順(デフォルト)
SELECT * FROM users ORDER BY salary;

-- 給与の降順
SELECT * FROM users ORDER BY salary DESC;

-- まず都市で並べ、同じ都市の中では給与を降順にする
SELECT * FROM users ORDER BY city, salary DESC;

なぜ ORDER BY は最後に書くことが多いのか?

並び替えは次のようなものだからです。

  • まず結果を出して、最後にどう見せるかを決める

これは、

  • 先に絞り込む
  • 先にグループ化する

という処理とは、同じ段階ではありません。


四、集計関数とグループ化(GROUP BY)

よく使う集計関数

関数役割
COUNT(*)件数を数えるレコードが全部で何件あるか
SUM(col)合計を出す総給与
AVG(col)平均を出す平均年齢
MAX(col)最大値を出す最高給与
MIN(col)最小値を出す最低年齢
-- 基本的な集計
SELECT COUNT(*) AS 総人数, AVG(salary) AS 平均給与, MAX(salary) AS 最高給与
FROM users;

GROUP BY:グループごとに集計する

-- 都市ごとに人数と平均給与を集計
SELECT city, COUNT(*) AS 人数, AVG(salary) AS 平均給与
FROM users
GROUP BY city;
city | 人数 | 平均給与
北京 | 2 | 25000.0
上海 | 2 | 20000.0
广州 | 1 | 8000.0
深圳 | 1 | 12000.0

HAVING:グループ化した結果を絞り込む

-- 平均給与が 15000 を超える都市を探す
SELECT city, AVG(salary) AS avg_salary
FROM users
GROUP BY city
HAVING avg_salary > 15000;
WHEREHAVING の違い
  • WHERE はグループ化に絞り込む(元の行を絞る)
  • HAVING はグループ化に絞り込む(集計結果を絞る)

SQL の実行順

SQL は、書く順番と実行順が違います。


五、複数テーブルの結合(JOIN)

JOIN は SQL の最も強力な機能の1つで、複数のテーブルのデータをまとめることができます。

INNER JOIN:内部結合

両方のテーブルに一致する行だけを返します。

-- 各ユーザーの注文情報を検索
SELECT users.name, orders.product, orders.amount
FROM users
INNER JOIN orders ON users.id = orders.user_id;
name | product | amount
张三 | iPhone | 7999.0
张三 | AirPods | 999.0
李四 | MacBook | 14999.0
王五 | iPad | 3999.0
李四 | キーボード | 599.0
赵六 | モニター | 2999.0
钱七 | マウス | 299.0

注意:孙八 には注文がないので、結果には出てきません。

LEFT JOIN:左結合

左側のテーブルの行をすべて返し、右側で一致しないものは NULL になります。

-- すべてのユーザーとその注文を検索する(注文がないユーザーも表示)
SELECT users.name, orders.product, orders.amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
name | product | amount
张三 | iPhone | 7999.0
张三 | AirPods | 999.0
李四 | MacBook | 14999.0
...
孙八 | None | None ← 注文がないが、表示されている

JOIN の種類の比較

SQL の JOIN と Pandas の merge
SQLPandas
INNER JOINpd.merge(how="inner")
LEFT JOINpd.merge(how="left")
RIGHT JOINpd.merge(how="right")
ON users.id = orders.user_idon="user_id" または left_on=, right_on=

実用的な組み合わせ:JOIN + GROUP BY

-- 各ユーザーの注文総額を検索
SELECT users.name, COUNT(orders.order_id) AS 注文数, SUM(orders.amount) AS 総消費額
FROM users
LEFT JOIN orders ON users.id = orders.user_id
GROUP BY users.id, users.name
ORDER BY 総消費額 DESC;

六、追加・更新・削除(INSERT / UPDATE / DELETE)

データを追加する

-- 1件追加
INSERT INTO users (name, age, city, salary) VALUES ('周九', 29, '杭州', 16000);

-- 複数件追加
INSERT INTO users (name, age, city, salary) VALUES
('吴十', 33, '成都', 13000),
('郑十一', 27, '南京', 11000);

データを更新する

-- 张三 に昇給を反映する
UPDATE users SET salary = 18000 WHERE name = '张三';

-- 北京の社員全員を10%昇給する
UPDATE users SET salary = salary * 1.1 WHERE city = '北京';
UPDATE には必ず WHERE を付けよう!

UPDATE users SET salary = 0; を実行すると、全ユーザーの給与が 0 になります。WHERE を付け忘れるのは、データベース操作で最もよくある事故の1つです。

データを削除する

-- 指定したレコードを削除
DELETE FROM users WHERE name = '周九';

-- 20歳未満をすべて削除
DELETE FROM users WHERE age < 20;
DELETEWHERE を付けよう!

DELETE FROM users; を実行すると、テーブルのすべてのデータが削除されます。実行前によく考えてください。

SQL を最初に学ぶときの、いちばん安定した順番

基本的には、次の順番が学びやすいです。

  1. まず SELECT / FROM / WHERE をしっかり書けるようにする
  2. 次に ORDER BY を足す
  3. 次に GROUP BY / HAVING を足す
  4. 最後に JOIN と追加・更新・削除を学ぶ

最初から全部の文法ブロックを覚えようとするより、ずっと混乱しにくくなります。


SQL 文の速習表

操作SQL 文法説明
全件検索SELECT * FROM 表名すべてのデータを取る
指定列の検索SELECT 列1, 列2 FROM 表名一部の列を取る
条件フィルタSELECT ... WHERE 条件行を絞り込む
並び替えORDER BY 列 DESC降順に並べる
件数制限LIMIT 10先頭 N 件を取る
重複除去SELECT DISTINCT 列一意な値を取る
集計COUNT / SUM / AVG / MAX / MIN統計計算をする
グループ化GROUP BY 列グループごとに集計する
グループの絞り込みHAVING 条件集計結果を絞る
内部結合INNER JOIN 表 ON 条件2つの表の共通部分
左結合LEFT JOIN 表 ON 条件左表をすべて返す
追加INSERT INTO 表 VALUES (...)データを追加する
更新UPDATE 表 SET 列=値 WHERE 条件データを変更する
削除DELETE FROM 表 WHERE 条件データを削除する

まとめ

SQL はデータベースと「会話する」ための言語です。核になるのは次の4種類の操作です。

分類キーワード役割
検索SELECTデータを検索する(最もよく使う)
追加INSERT新しいデータを入れる
更新UPDATE既存データを変更する
削除DELETEデータを削除する

この中でも、SELECTWHEREJOINGROUP BY を組み合わせると、データ分析のほとんどの要求に対応できます。

この節でいちばん持ち帰ってほしいこと

  • SQL で大事なのは、キーワードの数ではなく、表に安定して質問できるかどうか
  • 「どの列がほしいか、どの行がほしいか、どう分けるか」を先に考えてから SQL を書くと、文法を丸暗記するよりずっと安定する
  • WHERE / GROUP BY / JOIN の3つの考え方がわかると、その後の多くのクエリは分解して理解できる

手を動かして練習しよう

練習 1:基本検索

-- 上の練習用データベースを使って、次の検索を完成させてください。
-- 1. 上海のユーザーをすべて検索
-- 2. 給与が高い順に上位3人を検索
-- 3. 都市ごとの平均給与を検索し、平均給与の高い順に並べる

練習 2:JOIN 検索

-- 1. すべてのユーザーの名前と、購入した商品を検索
-- 2. 注文したことがないユーザーを検索
-- ヒント:LEFT JOIN + WHERE orders.order_id IS NULL
-- 3. 注文がないユーザーも含めて、ユーザーごとの注文総額を検索する(0 と表示)

練習 3:総合分析

-- 1本の SQL で次を完成させてください:
-- 総消費額が 5000 を超えるユーザーの名前、注文数、総消費額を検索
-- 総消費額の高い順に並べる