
データ分析・自動レポート・簡易アプリの土台はSQLです。Pythonだけで押し切るより、前処理の7割をDB側で完結させる方が速く・安定します。本記事は、未経験〜初学者が週10時間×2〜3週で、SELECT→WHERE→GROUP BY→JOIN→ウィンドウ関数→CTE→最適化(EXPLAIN/INDEX)までを“実務の型”で習得できるよう設計。すべてコピペで動くサンプルと、月末ミニプロジェクト、レビュー観点チェックリストを配布します。
この記事で身に付く力
- 業務で崩れないSQLの“型”(抽出→集計→結合→窓関数→最適化)
- 読める・直せるクエリの書き方(CTEで段階化/粒度の統一)
- EXPLAIN/INDEXの最短実務運用(遅いSQLを早くする勘所)
SQLでつまずく典型パターン
最初の壁は“用語”ではなく“粒度”と“結合”。現場で頻発する落とし穴を先に知っておくと、学習効率が一気に上がります。
GROUP BY
と集計関数の関係が曖昧で、想定外の行数になる。INNER/LEFT JOIN
の使い分けを誤り、行が欠落する/爆増する。ROW_NUMBER
やRANK
などウィンドウ関数のPARTITION/ORDER
設計がふわっとして誤集計。- インデックスの基本があやふやで、遅いクエリを連発。
解決策は、「目的→テーブル関係→クエリの型」で考えること。最頻出パターンを型化すれば迷いが消えます。
[内部リンク:未経験からデータサイエンティストへ:6ヶ月ロードマップ]
現場10年で残った“読みやすく速いSQL”の作法
筆者(ふみと)は大手企業でデータ/マーケティングサイエンティスト10年。DWH・BI・MLの現場で読めるSQL=強いを痛感しました。ここでは商用DB/クラウドでも通用する書き方に寄せつつ、ローカルでも動かせるようSQLite/PostgreSQL準拠の構文を中心に紹介します(方言差は都度注釈)。
[内部リンク:【自己紹介】大手企業データサイエンティスト10年のキャリアと学び]
SQL“実務の型” 12ステップ
以降はSQLiteを想定。ファイル1つで動くので学習に最適です(PostgreSQLでも大筋同じ)。
ステップ0:サンプルDBの用意(売上とマスタ)
sales
(order_date DATE, store TEXT, product TEXT, qty INT, price REAL
)/stores
(store TEXT PRIMARY KEY, area TEXT, open_date DATE
)を作り、最小構成で練習します。
-- DB作成(sqlite3 ターミナル例)
-- sqlite3 retail.sqlite
CREATE TABLE IF NOT EXISTS sales (
order_date TEXT,
store TEXT,
product TEXT,
qty INTEGER,
price REAL
);
CREATE TABLE IF NOT EXISTS stores (
store TEXT PRIMARY KEY,
area TEXT,
open_date TEXT
);
-- 例データ(簡略)
INSERT INTO stores VALUES
('S01','Tokyo','2023-01-01'),('S02','Osaka','2023-02-01');
INSERT INTO sales VALUES
('2024-04-01','S01','A',2,500),('2024-04-02','S01','B',1,1200),
('2024-04-02','S02','A',3,480),('2024-05-01','S01','A',5,500);
1) SELECT/WHERE/ORDER/LIMIT:抽出の基本
まずはWHEREで行を絞る→SELECTで列を選ぶ→ORDERで並べる。先にフィルタ、後で表示が鉄則です。
-- 売上データの基本列と計算列(amount=qty*price)
SELECT order_date, store, product,
qty, price,
qty * price AS amount
FROM sales
WHERE qty > 0 AND price > 0
ORDER BY order_date ASC
LIMIT 10;
2) 集計(GROUP BY/HAVING):粒度を意識
集計は「何の1行か?」を先に決める。SELECTに出す非集計列はすべてGROUP BYへ。条件は行→WHERE、集計後→HAVING。
-- 月×店舗の売上合計/数量
SELECT strftime('%Y-%m', order_date) AS ym,
store,
SUM(qty*price) AS sales,
SUM(qty) AS qty
FROM sales
WHERE qty>0 AND price>0
GROUP BY ym, store
HAVING SUM(qty) > 0
ORDER BY ym, store;
3) JOIN(INNER/LEFT):“欠落させない”結合
欠けて困る側をLEFTの左に。参照マスタのキー重複は事前に検査([内部リンク:pandas実践]の検査手法参照)。
-- 売上に店舗のエリアを付与(LEFTで欠落防止)
SELECT s.order_date, s.store, t.area,
s.product, s.qty, s.price,
s.qty*s.price AS amount
FROM sales AS s
LEFT JOIN stores AS t
ON s.store = t.store;
4) サブクエリ/CTE:読みやすさの味方
複雑な集計はCTEで段階化。FROM句サブクエリで局所的に処理すると読みやすいです。
-- CTEで月次集計→上位店舗を抽出
WITH monthly AS (
SELECT strftime('%Y-%m', order_date) AS ym, store,
SUM(qty*price) AS sales
FROM sales
GROUP BY ym, store
)
SELECT ym, store, sales
FROM (
SELECT ym, store, sales,
ROW_NUMBER() OVER(PARTITION BY ym ORDER BY sales DESC) AS rn
FROM monthly
)
WHERE rn <= 3
ORDER BY ym, rn;
5) ウィンドウ関数:ランキング/移動平均/累積
PARTITION BY
=グループ、ORDER BY
=時間/順位の方向。LAG/LEAD/SUM OVERは最頻出。
-- 店舗別の売上累積と前月比
WITH m AS (
SELECT strftime('%Y-%m', order_date) AS ym, store,
SUM(qty*price) AS sales
FROM sales
GROUP BY ym, store
)
SELECT ym, store, sales,
SUM(sales) OVER(PARTITION BY store ORDER BY ym) AS sales_cum,
LAG(sales, 1) OVER(PARTITION BY store ORDER BY ym) AS sales_prev,
ROUND( (sales - LAG(sales,1) OVER(PARTITION BY store ORDER BY ym))
/ NULLIF(LAG(sales,1) OVER(PARTITION BY store ORDER BY ym),0), 4) AS yoy
FROM m
ORDER BY store, ym;
6) CASE式:条件分岐(ラベル付け/バケット)
可読性のため、閾値やラベルは上から下へ。後で辞書テーブル化も検討。
SELECT product,
CASE
WHEN price >= 1000 THEN 'High'
WHEN price >= 500 THEN 'Mid'
ELSE 'Low'
END AS price_band,
COUNT(*) AS cnt
FROM sales
GROUP BY product, price_band;
7) 重複/NULL/文字列処理の定石
NULLは早めに埋める/除外。文字列の正規化は入力時 or 取り出し直後。
-- 重複除去
SELECT DISTINCT store FROM sales;
\-- NULLを0に
SELECT IFNULL(SUM(qty), 0) FROM sales;
\-- 文字列整形(SQLite)
SELECT TRIM(REPLACE(product, ' ', '' )) AS product\_norm FROM sales;
8) セット演算(UNION/INTERSECT/EXCEPT)
集合演算は存在比較に強い。UNION ALLは重複許容、UNIONは重複削除。
-- 2期間の共通/差分店舗(例)
WITH s1 AS (
SELECT DISTINCT store FROM sales WHERE order_date BETWEEN '2024-04-01' AND '2024-04-30'
), s2 AS (
SELECT DISTINCT store FROM sales WHERE order_date BETWEEN '2024-05-01' AND '2024-05-31'
)
SELECT 'common' AS kind, store FROM (
SELECT store FROM s1 INTERSECT SELECT store FROM s2
)
UNION ALL
SELECT 'only_apr', store FROM (
SELECT store FROM s1 EXCEPT SELECT store FROM s2
)
UNION ALL
SELECT 'only_may', store FROM (
SELECT store FROM s2 EXCEPT SELECT store FROM s1
);
9) パフォーマンス基本:EXPLAINとインデックス
フィルタ/結合キーにインデックス。複合は選択性の高い列→範囲列の順が目安。
-- 実行計画(SQLite)
EXPLAIN QUERY PLAN
SELECT *
FROM sales
WHERE store='S01' AND order_date BETWEEN '2024-04-01' AND '2024-04-30';
\-- 複合インデックス(フィルタ順に)
CREATE INDEX IF NOT EXISTS idx\_sales\_store\_date ON sales(store, order\_date);
10) 取引(トランザクション)と排他の最短メモ
一貫性の必要な一連の変更はBEGIN…COMMIT
でまとめる。失敗時はROLLBACK
。
BEGIN TRANSACTION;
INSERT INTO sales VALUES('2024-06-01','S02','B',4,1200);
UPDATE stores SET area='Kansai' WHERE store='S02';
COMMIT;
11) Python連携:pandas×SQLiteで実務導線
重い整形はSQL、最終可視化/配布はPython/Excelが最速です。
import sqlite3, pandas as pd
con = sqlite3.connect("retail.sqlite")
q = """
WITH monthly AS (
SELECT strftime('%Y-%m', order_date) AS ym, store,
SUM(qty*price) AS sales, SUM(qty) AS qty
FROM sales
WHERE qty>0 AND price>0
GROUP BY ym, store
)
SELECT * FROM monthly;
"""
monthly = pd.read_sql_query(q, con)
monthly.to_excel("report.xlsx", index=False)
con.close()
12) 品質保証:レビュー観点チェックリスト
提出前の最終チェックに。レビュー観点が明文化されていると事故が減ります。
- 粒度が明確(行=何の1行か?)
-
INNER/LEFT
の選択根拠がある(欠落の許容) -
GROUP BY
列と集計関数の対応が正しい - ウィンドウ関数の
PARTITION/ORDER
が妥当 - インデックスの有無(結合キー/フィルタ列)を確認
- CTEで段階化され、読みやすい
スクール伴走で“仕上げ”まで最短到達
独学でもいけますが、設計レビューと質問対応があると、JOINやウィンドウの事故率が激減。6ヶ月ロードマップのポートフォリオに直結させるなら、下記2校の無料カウンセリングから始めるのが近道です。
- 株式会社キカガク:業務再現型の課題設計と出口支援が強い。転職直結に◎。
- Tech Academy:質問の速さ×短時間運用が強み。副業/在宅と相性◎。
TechAcademy データサイエンスコース(受講料:174,600円~ ※更に割引あり)

株式会社キカガク AI人材長期育成コース(受講料:237,600円~)

行動:ミニプロジェクト(提出推奨)
課題:「月次×店舗の売上トレンド、上位3店舗、前年同月比を1本のSQLで出力し、report.xlsx
に保存」。
sales
からamount=qty*price
、ym=strftime('%Y-%m', order_date)
を作成。WITH monthly AS (...)
で月次集計テーブルを作る。- ウィンドウで
ROW_NUMBER()
とLAG()
を使い、上位3/前年比を併記。 - Pythonで
pd.read_sql_query
→Excel出力。 - READMEに再現手順と示唆(3行)を記載。
WITH monthly AS (
SELECT strftime('%Y-%m', order_date) AS ym,
store,
SUM(qty*price) AS sales
FROM sales
WHERE qty>0 AND price>0
GROUP BY ym, store
), ranked AS (
SELECT ym, store, sales,
ROW_NUMBER() OVER(PARTITION BY ym ORDER BY sales DESC) AS rn,
LAG(sales,1) OVER(PARTITION BY store ORDER BY ym) AS prev
FROM monthly
)
SELECT ym, store, sales, rn,
ROUND( (sales - prev) / NULLIF(prev,0), 4) AS yoy
FROM ranked
WHERE rn <= 3
ORDER BY ym, rn;
[ ] 粒度とJOIN/LEFTの根拠が言語化されている
[ ] GROUP BY/HAVING/WHEREの使い分けが正しい
[ ] PARTITION/ORDERが要件に合致
[ ] 実行計画(EXPLAIN)でフルスキャンを避けられている
[ ] 再現手順(DB作成→SQL→出力)がREADMEにある
付録A:SQLチートシート(保存版)
-- 1) Top-N per group
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY group_col ORDER BY score DESC) AS rn
FROM t
) WHERE rn<=N;
\-- 2) ピボット風(集計を列化)
SELECT ym,
SUM(CASE WHEN store='S01' THEN sales ELSE 0 END) AS S01,
SUM(CASE WHEN store='S02' THEN sales ELSE 0 END) AS S02
FROM monthly GROUP BY ym;
\-- 3) EXISTS で高速な存在判定
SELECT \* FROM t1 WHERE EXISTS (
SELECT 1 FROM t2 WHERE t2.key=t1.key
);
\-- 4) 日付関数(SQLite)
SELECT date(order\_date,'start of month') AS first\_day FROM sales;
付録B:SQLスタイルガイド(最短版)
- すべて大文字キーワード、小文字テーブル/列で可読性UP
- 列は論理順に並べる(キー→属性→計算列)
- CTE名/エイリアスは意味を持たせる(
monthly
,ranked
) - コメントは節ごとに(
-- 目的
/-- 集計
/-- ランキング
) - 1行は100文字前後、句ごとに改行
付録C:安全な実装のために
- アプリからはプレースホルダでパラメータ化(SQLインジェクション防止)
- 本番DBは読み取り専用アカウントで参照
- 個人情報は最小限の列だけ取得、マスキング運用
この記事から次に読むべきもの(内部リンク)
-
-
【保存版】SQLite×Pythonで作る“ローカルDWH”——ETL・集計・レポート自動化の最短手順
ローカルでゼロ構築、ファイル1つで完結、サーバ不要。本記事はSQLite×Pythonで“毎日回る”ETL・集計・レポート自動化を最短で作るための完全ガイドです。データ設計→DB作成→ETL(取り込み ...
-
-
【実務で差がつく】pandas実践:欠損処理・結合・ウィンドウ関数・時系列・品質保証まで“読みやすく速い”型を習得
リード(結論)基礎を終えたら次は実務の現場で頻出する処理を“型”で覚える段階です。本記事は、pandas 2.x を前提に、欠損・外れ値・結合・ウィンドウ関数・時系列・カテゴリ処理・集計の自動化・大規 ...
-
-
【保存版】可視化入門:Matplotlib/Plotlyの使い分けと“伝わるグラフ設計”10ステップ
結論:可視化は「きれいに描く」ことではなく、意思決定を動かすための設計です。本稿では、未経験〜初学者が 週10時間×1〜2週 で、Matplotlib/Plotlyを軸に “伝わるグラフ”の設計と実装 ...
-
-
【保存版】データレポート納品の型:要件定義→ETL→検証→可視化→Excel/PDF→引き継ぎまで、失注しないワークフロー完全版
“いい分析”より“伝わる納品”。副業や実務で評価されるのは、意思決定に効く1枚と再現できるパッケージを期限通り出せること。 本記事は、未経験〜初学者が週10時間×2〜3週で、要件定義→データ受領→ET ...
-
-
【保存版】scikit-learn基礎:回帰・分類・前処理・パイプライン・交差検証を“実務の型”で習得
機械学習で迷子になる最大の理由は、前処理→学習→評価→改善の順番が曖昧なまま個々のアルゴリズムに飛びつくこと。本記事は、未経験〜初学者が週10時間×2〜3週で到達できるscikit-learnの最短ル ...
最近のコメント