
データ分析・自動レポート・簡易アプリの土台は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設計が曖昧で誤集計。- インデックスの基本が曖昧で、遅いクエリを量産。
👉 解決は 「目的 → テーブル関係 → クエリの型」の順で考えること。最頻出パターンを型化すれば迷いが減ります。
現場10年で残った“読みやすく速いSQL”の作法
商用DB/クラウドでも通用する書き方に寄せつつ、ローカルでも動かせる SQLite / PostgreSQL 準拠で解説します(方言差は都度注釈)。キモは「段階化(CTE)」「粒度の明示」「結合の根拠」。
SQL“実務の型” 12ステップ
まずは ファイル1つのSQLite で手を動かし、後でPostgreSQLに置き換えるのが最短です(PostgreSQLでも大筋同じ)。
ステップ0:サンプルDBの用意(売上とマスタ)
何の1行か(粒度)を意識した最小データで練習します。
-- 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明細)を決めた上で、後続の集計・結合を試せる形を用意します。
1) SELECT/WHERE/ORDER/LIMIT:抽出の基本
**先に絞る(WHERE)→後で見せる(SELECT)**が鉄則。
-- 売上データの基本列と計算列(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;
ポイント:計算列は AS で意味のある別名を付けておくと、後工程(PythonやBI)で迷いません。
2) 集計(GROUP BY/HAVING):粒度を意識
「何の1行か?」(粒度)を先に決めてからSELECTを書きます。
非集計列はすべてGROUP BYへ。
-- 月×店舗の売上合計/数量
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;
使い分け:行に対する条件は WHERE、集計結果に対する条件は HAVING。
3) JOIN(INNER/LEFT):“欠落させない”結合
「欠けて困る側」を LEFT の左テーブルに置きます。参照マスタのキー重複は事前検査。
-- 売上に店舗のエリアを付与(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;
根拠の言語化:なぜLEFTなのか(欠落の許容範囲)を説明できるようにします。
4) サブクエリ/CTE:読みやすさの味方
複雑な処理は段階化して“意味のある中間表”を積み上げます。
-- 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;
読みやすさ:monthly のように意味のあるCTE名を付けるとレビューが楽になります。
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;
注意:前月が0のときは NULLIF(..., 0) でゼロ割り回避。
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は早めに埋める/除外。文字列は正規化してから使うのが鉄則。
-- 重複除去
SELECT DISTINCT store FROM sales;
\-- NULLを0に
SELECT IFNULL(SUM(qty), 0) FROM sales;
\-- 文字列整形(SQLite)
SELECT TRIM(REPLACE(product, ' ', '' )) AS product\_norm FROM sales;
補足:PostgreSQLでは COALESCE、TRIM/REPLACE は同名で利用可能です。
8) セット演算(UNION/INTERSECT/EXCEPT)
期間比較や存在チェックに強いパターンです。
-- 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
);
読み解き:INTERSECT は共通集合、EXCEPT は差集合です。
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;
ねらい:更新処理の途中失敗でもDBの整合性を守ります。
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()
やっていること:SQLで月次テーブルを作り、pandasで読み込んでExcelに出力しています。
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;
付録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スタイルガイド(最短版)
- キーワードは大文字、テーブル/列は小文字
- 列は論理順(キー → 属性 → 計算列)
- 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週 で、要件定義 → データ受 ...
-
-
【保存版】scikit-learn基礎:回帰・分類・前処理・パイプライン・交差検証を“実務の型”で習得
機械学習で迷子になる最大の理由は、前処理→学習→評価→改善の順番が曖昧なまま個々のアルゴリズムに飛びつくこと。 本記事は、未経験〜初学者が週10時間×2〜3週で到達できるscikit-learnの最短 ...
最近のコメント