100本ノック / SQL / データ分析のためのSQL入門100本ノック
検査記録DBからSQLで不良データを抽出・分析する
検査記録DBからSQLで不良データを抽出・分析する
SQL 100本ノック 第1章(No.001〜No.010):SQLの基本
本記事は「データ分析のための SQL 入門 100本ノック」シリーズの 第1章 です。
SQL の最も基本的な命令であるSELECT文から始め、
FROM・LIMIT・ORDER BY・DISTINCTなど、
データ取得の基礎を製造業の品質管理データを通して体系的に学びます。
[!NOTE] 本資料は、数理工房(もしくは代表である和山個人)が過去に企業研修において使用した notebook を
企業様の許可を得て再構成・編集のうえ公開しています。
掲載データはすべて架空のものであり、実在する企業・工場・数値とは一切関係ありません。
はじめに:この記事で扱う製造業の実務課題
自動車部品メーカーの品質管理部 田中さんのケースです。
月次品質報告書の作成(毎月月初の定例業務)
1. 品質管理システムの DB から1ヶ月分の検査記録を取得
2. ライン別・部品別の不良数を集計してエクスポート
3. 不良数が多い工程をランキング形式で特定
4. アクションアイテムを添えて経営会議向けに提出
現在は CSV を Excel に貼り付けてフィルタ・ソートを手作業で行っているため、
月初に 毎月2〜3時間 かかっています。
SQL を使えば「ライン別不良数を多い順に並べる」「特定ラインのデータだけ抽出する」
といった操作が 1クエリ(数秒)で完了します。
本章では SQL の基本から始め、この効率化を実現するための土台を築きます。
現場でよくある状況
| 場面 | 現状の課題 | SQL で解決できること |
|---|---|---|
| 不良数の確認 | 全件 CSV を開いて Excel でソート(毎回) | ORDER BY defect_qty DESC で即座に降順表示 |
| 部品種類の把握 | 部品コードを目視でカウント | SELECT DISTINCT part_code でユニーク一覧 |
| 列の絞り込み | 不要列を削除してから保存 | SELECT で必要な列だけ指定 |
| 先頭確認 | ファイルを全部開かないと内容が分からない | LIMIT 5 で先頭5件だけ確認 |
| 特定情報の抽出 | フィルタ設定を毎回やり直し | クエリを保存して再実行 |
SQL を知ると、上記すべての操作が 5〜10行以内のクエリ で完結します。
さらに、クエリをファイルに保存しておけば 来月も同じ手順を1秒で再現 できます。
なぜこの問題は判断が難しいのか
SQL 初学者がつまずきやすい4つのポイントを先に整理しておきます。
1. 書く順序と実行される順序が異なる
SQL は SELECT → FROM → WHERE → ORDER BY → LIMIT の順で 書きます が、
実際に DB エンジンが処理する順序は FROM → WHERE → SELECT → ORDER BY → LIMIT です。
この違いを知らないと「なぜこのクエリはエラーになるのか」が分からず、
デバッグに時間がかかります。(No.010 で詳しく解説します)
2. SELECT * の落とし穴
全列を取得する SELECT * は探索には便利ですが、
テーブルに列が増えると不要なデータも全て読み込むため、
速度低下・可読性低下の原因になります。
本番クエリでは必要な列だけを明示的に指定するのが原則です。
3. ORDER BY なしの返却順序は保証されない
ORDER BY を指定しない場合、レコードの返却順はデータベースエンジンに依存します。
「常に同じ順番で返ってくる」と思い込んでいると、
データが増えたタイミングで順序が変わり、分析結果が変動します。
4. NULL の扱いが直感と異なる
SQL の NULL は「値が存在しない」を意味し、
NULL = NULL の比較結果は TRUE ではなく UNKNOWN になります。
(第2章 No.020 で詳しく扱います)
今回扱うノックの全体像
| No. | タイトル | 製造業での活用場面 |
|---|---|---|
| 001 | SELECT文の基本を理解する | DBから最初のデータ取得 |
| 002 | FROM句でテーブルを指定する | 複数テーブル(検査記録 / 部品マスタ)の使い分け |
| 003 | すべての列を取得する | データ構造の素早い確認(探索的分析) |
| 004 | 必要な列だけを取得する | 報告書用データの列絞り込み |
| 005 | 列に別名を付ける | 英語列名を日本語ラベルで出力 |
| 006 | 取得件数を制限する | 大量データの先頭N件確認 |
| 007 | ORDER BYで並び替える | 不良数の多い工程を先頭に表示 |
| 008 | 昇順・降順を指定する | ワースト / ベスト順の切り替え |
| 009 | DISTINCTで重複を除外する | 検査対象の部品コード種類を把握 |
| 010 | SQLの実行順序を理解する | バグの原因となる「書き順≠実行順」の把握 |
Python 環境の準備
import subprocess, sys
res = subprocess.run(["sw_vers", "-productVersion"], capture_output=True, text=True)
print(f"macOS : {res.stdout.strip()}")
print(f"Python: {sys.version}")
macOS : 26.3
Python: 3.13.1 (main, Dec 3 2024, 17:59:52) [Clang 16.0.0 (clang-1600.0.26.4)]
import sqlite3
import polars as pl
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.rcParams['font.family'] = 'Hiragino Maru Gothic Pro'
%config InlineBackend.figure_format = 'svg'
print(f"sqlite3 : {sqlite3.sqlite_version}")
print(f"polars : {pl.__version__}")
print(f"numpy : {np.__version__}")
print(f"matplotlib: {matplotlib.__version__}")
print()
print("ライブラリ読み込み完了")
sqlite3 : 3.47.2
polars : 1.42.1
numpy : 2.5.1
matplotlib: 3.11.0
ライブラリ読み込み完了
架空データの作成
想定シナリオ: 自動車部品メーカー 精密加工工場 / 品質管理部
分析期間: 2024年1月(稼働日 約15日分、全30件の検査記録)
テーブル構成: 2テーブル
| テーブル名 | 説明 | 主なカラム |
|---|---|---|
inspections | 検査記録テーブル(ファクトテーブル) | 検査日・ライン・部品コード・生産数・不良数 |
parts_master | 部品マスタテーブル(マスタテーブル) | 部品コード・部品名・カテゴリ・単価・仕入先 |
Python 標準ライブラリの sqlite3 でインメモリDBに作成します(外部ファイル不要)。
# ──────────────────────────────────────────────────────
# SQL ヘルパー関数:SQL を表示して実行し Polars DataFrame を返す
# ──────────────────────────────────────────────────────
def q(conn, sql):
'''SQLを実行して Polars DataFrame で結果を表示する'''
print('── SQL ─────────────────────────────────────────')
for line in sql.strip().split('\n'):
print(f' {line}')
print('───────────────────────────────────────────────')
cur = conn.execute(sql.strip())
rows = cur.fetchall()
cols = [d[0] for d in cur.description]
data = {col: [row[i] for row in rows] for i, col in enumerate(cols)}
df = pl.DataFrame(data)
print(df)
print(f'↳ {len(rows)} 行取得')
return df
# ──────────────────────────────────────────────────────
# インメモリ SQLite データベース作成
# ──────────────────────────────────────────────────────
conn = sqlite3.connect(':memory:')
# 検査記録テーブル
conn.execute('''
CREATE TABLE inspections (
id INTEGER PRIMARY KEY,
inspection_date TEXT NOT NULL,
line_code TEXT NOT NULL,
part_code TEXT NOT NULL,
part_name TEXT NOT NULL,
category TEXT NOT NULL,
shift TEXT NOT NULL,
production_qty INTEGER NOT NULL,
defect_qty INTEGER NOT NULL,
inspector_code TEXT NOT NULL
)
''')
# 部品マスタテーブル
conn.execute('''
CREATE TABLE parts_master (
part_code TEXT PRIMARY KEY,
part_name TEXT NOT NULL,
category TEXT NOT NULL,
unit_price INTEGER NOT NULL,
supplier TEXT NOT NULL
)
''')
# 検査記録データ(30件)
inspections_data = [
(1, '2024-01-04', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '早番', 450, 3, 'INS-001'),
(2, '2024-01-04', 'LINE-A2', 'ENG-002', 'クランクシャフト', 'エンジン部品', '早番', 220, 5, 'INS-002'),
(3, '2024-01-04', 'LINE-B1', 'BRK-001', 'ブレーキパッド', 'ブレーキ部品', '早番', 380, 2, 'INS-003'),
(4, '2024-01-05', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '遅番', 460, 7, 'INS-001'),
(5, '2024-01-05', 'LINE-A2', 'ENG-002', 'クランクシャフト', 'エンジン部品', '遅番', 215, 4, 'INS-002'),
(6, '2024-01-05', 'LINE-B2', 'BRK-002', 'ブレーキキャリパー', 'ブレーキ部品', '早番', 180, 6, 'INS-004'),
(7, '2024-01-08', 'LINE-C1', 'ELC-001', 'オルタネータ', '電装部品', '夜間', 120, 8, 'INS-005'),
(8, '2024-01-08', 'LINE-B1', 'BRK-001', 'ブレーキパッド', 'ブレーキ部品', '遅番', 390, 4, 'INS-003'),
(9, '2024-01-09', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '早番', 470, 2, 'INS-001'),
(10, '2024-01-09', 'LINE-C1', 'ELC-002', 'スタータモータ', '電装部品', '早番', 100, 3, 'INS-005'),
(11, '2024-01-10', 'LINE-A2', 'SUS-001', 'ショックアブソーバ', 'サスペンション', '遅番', 90, 9, 'INS-002'),
(12, '2024-01-10', 'LINE-B2', 'BRK-002', 'ブレーキキャリパー', 'ブレーキ部品', '早番', 175, 5, 'INS-004'),
(13, '2024-01-11', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '早番', 455, 4, 'INS-001'),
(14, '2024-01-11', 'LINE-B1', 'BRK-001', 'ブレーキパッド', 'ブレーキ部品', '夜間', 360, 12, 'INS-003'),
(15, '2024-01-12', 'LINE-C1', 'ELC-001', 'オルタネータ', '電装部品', '早番', 115, 6, 'INS-005'),
(16, '2024-01-15', 'LINE-A2', 'ENG-002', 'クランクシャフト', 'エンジン部品', '早番', 225, 3, 'INS-002'),
(17, '2024-01-15', 'LINE-B2', 'SUS-001', 'ショックアブソーバ', 'サスペンション', '遅番', 85, 7, 'INS-004'),
(18, '2024-01-16', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '夜間', 440, 15, 'INS-001'),
(19, '2024-01-16', 'LINE-C1', 'ELC-002', 'スタータモータ', '電装部品', '遅番', 105, 4, 'INS-005'),
(20, '2024-01-17', 'LINE-B1', 'BRK-001', 'ブレーキパッド', 'ブレーキ部品', '早番', 400, 3, 'INS-003'),
(21, '2024-01-18', 'LINE-A2', 'ENG-002', 'クランクシャフト', 'エンジン部品', '早番', 230, 6, 'INS-002'),
(22, '2024-01-18', 'LINE-B2', 'BRK-002', 'ブレーキキャリパー', 'ブレーキ部品', '夜間', 185, 9, 'INS-004'),
(23, '2024-01-19', 'LINE-C1', 'ELC-001', 'オルタネータ', '電装部品', '早番', 118, 5, 'INS-005'),
(24, '2024-01-22', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '早番', 465, 2, 'INS-001'),
(25, '2024-01-22', 'LINE-B1', 'SUS-001', 'ショックアブソーバ', 'サスペンション', '遅番', 88, 11, 'INS-003'),
(26, '2024-01-23', 'LINE-A2', 'ENG-002', 'クランクシャフト', 'エンジン部品', '早番', 210, 4, 'INS-002'),
(27, '2024-01-23', 'LINE-B2', 'BRK-001', 'ブレーキパッド', 'ブレーキ部品', '早番', 375, 2, 'INS-004'),
(28, '2024-01-24', 'LINE-C1', 'ELC-002', 'スタータモータ', '電装部品', '夜間', 98, 7, 'INS-005'),
(29, '2024-01-25', 'LINE-A1', 'ENG-001', 'ピストンリング', 'エンジン部品', '遅番', 450, 5, 'INS-001'),
(30, '2024-01-25', 'LINE-B2', 'BRK-002', 'ブレーキキャリパー', 'ブレーキ部品', '早番', 170, 4, 'INS-004'),
]
conn.executemany('INSERT INTO inspections VALUES (?,?,?,?,?,?,?,?,?,?)', inspections_data)
# 部品マスタデータ(7品種)
parts_data = [
('ENG-001', 'ピストンリング', 'エンジン部品', 1200, 'トヨタ精工'),
('ENG-002', 'クランクシャフト', 'エンジン部品', 8500, 'トヨタ精工'),
('BRK-001', 'ブレーキパッド', 'ブレーキ部品', 950, '住友ブレーキ'),
('BRK-002', 'ブレーキキャリパー', 'ブレーキ部品', 4200, '住友ブレーキ'),
('ELC-001', 'オルタネータ', '電装部品', 6800, 'デンソー'),
('ELC-002', 'スタータモータ', '電装部品', 3500, 'デンソー'),
('SUS-001', 'ショックアブソーバ', 'サスペンション', 2800, 'KYB'),
]
conn.executemany('INSERT INTO parts_master VALUES (?,?,?,?,?)', parts_data)
conn.commit()
print('データベース作成完了')
print(f' inspections テーブル: {conn.execute("SELECT COUNT(*) FROM inspections").fetchone()[0]} 件')
print(f' parts_master テーブル: {conn.execute("SELECT COUNT(*) FROM parts_master").fetchone()[0]} 件')
print()
print('テーブル一覧:')
for row in conn.execute("SELECT name, type FROM sqlite_master WHERE type='table' ORDER BY name").fetchall():
print(f' {row[1]:6s}: {row[0]}')
データベース作成完了
inspections テーブル: 30 件
parts_master テーブル: 7 件
テーブル一覧:
table : inspections
table : parts_master
# ライン別 生産数・不良数の棒グラフ(データ概要)
# ※ GROUP BY / SUM は第3章で詳しく学びます
rows = conn.execute('''
SELECT line_code,
SUM(production_qty) AS total_prod,
SUM(defect_qty) AS total_defect
FROM inspections
GROUP BY line_code
ORDER BY line_code
''').fetchall()
lines = [r[0] for r in rows]
total_prod = [r[1] for r in rows]
total_defect = [r[2] for r in rows]
x = list(range(len(lines)))
COLORS = ['#4878CF', '#6ACC65', '#D65F5F', '#B47CC7', '#C4AD66']
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
ax1 = axes[0]
bars1 = ax1.bar(x, total_prod, color=COLORS, alpha=0.85, edgecolor='black', linewidth=0.4)
for bar in bars1:
ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 30,
f'{bar.get_height():,}', ha='center', va='bottom', fontsize=9)
ax1.set_title('ライン別 2024年1月 合計生産数', fontsize=12, pad=10)
ax1.set_xlabel('生産ライン', fontsize=10)
ax1.set_ylabel('生産数(個)', fontsize=10)
ax1.set_xticks(x); ax1.set_xticklabels(lines, fontsize=9)
ax1.grid(axis='y', alpha=0.3)
ax2 = axes[1]
bars2 = ax2.bar(x, total_defect, color=COLORS, alpha=0.85, edgecolor='black', linewidth=0.4)
for bar in bars2:
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
f'{int(bar.get_height())}', ha='center', va='bottom', fontsize=9)
ax2.set_title('ライン別 2024年1月 合計不良数', fontsize=12, pad=10)
ax2.set_xlabel('生産ライン', fontsize=10)
ax2.set_ylabel('不良数(個)', fontsize=10)
ax2.set_xticks(x); ax2.set_xticklabels(lines, fontsize=9)
ax2.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('no_overview_line_stats.svg', format='svg', bbox_inches='tight')
plt.show()
print('データ概要グラフ保存完了: no_overview_line_stats.svg')
データ概要グラフ保存完了: no_overview_line_stats.svg
No.001:SELECT文の基本を理解する
実務での意味
SELECT 文は SQL でデータを取得するための基本命令です。
品質管理DBから「どの部品が不良数が多いか」「今月の検査記録を一覧で確認したい」
といった分析はすべて SELECT 文から始まります。
製造業での活用例:
- 検査記録から 部品名・不良数 だけを抽出して月次報告に使う
- 特定ラインの記録を取り出して品質会議の資料にする
分析・モデル化の考え方
SELECT 文の最小構成:
SELECT 列名1, 列名2, ...
FROM テーブル名;
- SELECT : 取得したい列(複数の場合はカンマ区切り)
- FROM : データの取得元テーブル
- SQL 文はセミコロン(
;)で終わりますが、Python のsqlite3では省略可能です - SQL キーワード(
SELECT・FROM)は大文字・小文字どちらでも動作しますが、
慣習として 大文字 で書くことで SQL キーワードとカラム名を区別します
Python で確認する
# No.001:SELECT 文の基本
# 部品名・シフト・不良数の3列を取得する
sql = '''
SELECT part_name, shift, defect_qty
FROM inspections
LIMIT 8
'''
q(conn, sql)
── SQL ─────────────────────────────────────────
SELECT part_name, shift, defect_qty
FROM inspections
LIMIT 8
───────────────────────────────────────────────
shape: (8, 3)
┌────────────────────┬───────┬────────────┐
│ part_name ┆ shift ┆ defect_qty │
│ --- ┆ --- ┆ --- │
│ str ┆ str ┆ i64 │
╞════════════════════╪═══════╪════════════╡
│ ピストンリング ┆ 早番 ┆ 3 │
│ クランクシャフト ┆ 早番 ┆ 5 │
│ ブレーキパッド ┆ 早番 ┆ 2 │
│ ピストンリング ┆ 遅番 ┆ 7 │
│ クランクシャフト ┆ 遅番 ┆ 4 │
│ ブレーキキャリパー ┆ 早番 ┆ 6 │
│ オルタネータ ┆ 夜間 ┆ 8 │
│ ブレーキパッド ┆ 遅番 ┆ 4 │
└────────────────────┴───────┴────────────┘
↳ 8 行取得
shape: (8, 3)
| part_name | shift | defect_qty |
|---|---|---|
| str | str | i64 |
| ”ピストンリング" | "早番” | 3 |
| ”クランクシャフト" | "早番” | 5 |
| ”ブレーキパッド" | "早番” | 2 |
| ”ピストンリング" | "遅番” | 7 |
| ”クランクシャフト" | "遅番” | 4 |
| ”ブレーキキャリパー" | "早番” | 6 |
| ”オルタネータ" | "夜間” | 8 |
| ”ブレーキパッド" | "遅番” | 4 |
結果の読み取り
SELECT part_name, shift, defect_qtyで指定した 3列だけ が返されます
(テーブルには10列あるが、必要な列だけを取り出せる)FROM inspectionsでどのテーブルから取得するかを明示していますLIMIT 8は先頭8件に制限するための修飾子です(No.006 で詳しく扱います)- 返却順序は挿入順(id=1, 2, 3 … の順)になっています
並び替えを保証したい場合はORDER BYが必要です(No.007 で学びます)
No.002:FROM句でテーブルを指定する
実務での意味
FROM 句は「どのテーブルからデータを取得するか」を指定します。
製造業のデータベースには通常、複数のテーブルが共存しています。
inspections(検査記録テーブル):日付・ライン・不良数などの実績データparts_master(部品マスタテーブル):部品コード・名称・カテゴリ・単価
目的に応じて FROM の後ろに適切なテーブル名を指定することで、
同じ SELECT 構文でも全く異なるデータを取得できます。
分析・モデル化の考え方
-- 検査記録テーブルから取得
SELECT ...
FROM inspections;
-- 部品マスタテーブルから取得(FROM 句を変えるだけ)
SELECT ...
FROM parts_master;
FROM に続くテーブル名は 大文字・小文字を区別します(DB エンジンによる)。
SQLite は大文字・小文字を区別しませんが、可読性のために統一表記を推奨します。
データベースに存在するテーブル一覧は SQLite では以下で確認できます:
SELECT name FROM sqlite_master WHERE type='table';
Python で確認する
# No.002:FROM 句 — テーブルを使い分ける
# ① 検査記録テーブルから取得
print('=== inspections テーブルから取得 ===')
sql1 = '''
SELECT part_code, part_name, production_qty, defect_qty
FROM inspections
LIMIT 5
'''
q(conn, sql1)
print()
# ② 部品マスタテーブルから取得(FROM 句を変えるだけ)
print('=== parts_master テーブルから取得 ===')
sql2 = '''
SELECT part_code, part_name, category, unit_price, supplier
FROM parts_master
'''
q(conn, sql2)
=== inspections テーブルから取得 ===
── SQL ─────────────────────────────────────────
SELECT part_code, part_name, production_qty, defect_qty
FROM inspections
LIMIT 5
───────────────────────────────────────────────
shape: (5, 4)
┌───────────┬──────────────────┬────────────────┬────────────┐
│ part_code ┆ part_name ┆ production_qty ┆ defect_qty │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ i64 ┆ i64 │
╞═══════════╪══════════════════╪════════════════╪════════════╡
│ ENG-001 ┆ ピストンリング ┆ 450 ┆ 3 │
│ ENG-002 ┆ クランクシャフト ┆ 220 ┆ 5 │
│ BRK-001 ┆ ブレーキパッド ┆ 380 ┆ 2 │
│ ENG-001 ┆ ピストンリング ┆ 460 ┆ 7 │
│ ENG-002 ┆ クランクシャフト ┆ 215 ┆ 4 │
└───────────┴──────────────────┴────────────────┴────────────┘
↳ 5 行取得
=== parts_master テーブルから取得 ===
── SQL ─────────────────────────────────────────
SELECT part_code, part_name, category, unit_price, supplier
FROM parts_master
───────────────────────────────────────────────
shape: (7, 5)
┌───────────┬────────────────────┬────────────────┬────────────┬──────────────┐
│ part_code ┆ part_name ┆ category ┆ unit_price ┆ supplier │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ str │
╞═══════════╪════════════════════╪════════════════╪════════════╪══════════════╡
│ ENG-001 ┆ ピストンリング ┆ エンジン部品 ┆ 1200 ┆ トヨタ精工 │
│ ENG-002 ┆ クランクシャフト ┆ エンジン部品 ┆ 8500 ┆ トヨタ精工 │
│ BRK-001 ┆ ブレーキパッド ┆ ブレーキ部品 ┆ 950 ┆ 住友ブレーキ │
│ BRK-002 ┆ ブレーキキャリパー ┆ ブレーキ部品 ┆ 4200 ┆ 住友ブレーキ │
│ ELC-001 ┆ オルタネータ ┆ 電装部品 ┆ 6800 ┆ デンソー │
│ ELC-002 ┆ スタータモータ ┆ 電装部品 ┆ 3500 ┆ デンソー │
│ SUS-001 ┆ ショックアブソーバ ┆ サスペンション ┆ 2800 ┆ KYB │
└───────────┴────────────────────┴────────────────┴────────────┴──────────────┘
↳ 7 行取得
shape: (7, 5)
| part_code | part_name | category | unit_price | supplier |
|---|---|---|---|---|
| str | str | str | i64 | str |
| ”ENG-001" | "ピストンリング" | "エンジン部品” | 1200 | ”トヨタ精工" |
| "ENG-002" | "クランクシャフト" | "エンジン部品” | 8500 | ”トヨタ精工" |
| "BRK-001" | "ブレーキパッド" | "ブレーキ部品” | 950 | ”住友ブレーキ" |
| "BRK-002" | "ブレーキキャリパー" | "ブレーキ部品” | 4200 | ”住友ブレーキ" |
| "ELC-001" | "オルタネータ" | "電装部品” | 6800 | ”デンソー" |
| "ELC-002" | "スタータモータ" | "電装部品” | 3500 | ”デンソー" |
| "SUS-001" | "ショックアブソーバ" | "サスペンション” | 2800 | ”KYB” |
結果の読み取り
FROM inspections: 検査実績データ(30件)が返されます
→ 日付・ライン・不良数など、現場の「実績値」が格納されているテーブルFROM parts_master: 7種類の部品マスタが返されます
→ 部品の属性情報(カテゴリ・単価・仕入先)が格納されているテーブル- 同じ
SELECT part_code, part_nameという列名でも、FROMの違いで得られる情報が異なります - 実務では
inspectionsのような実績テーブルを「ファクトテーブル」、
parts_masterのような属性テーブルを「マスタテーブル(ディメンションテーブル)」と呼びます
両者を結合するJOINは第5章(No.041〜050)で学びます
No.003:すべての列を取得する
実務での意味
SELECT * の *(アスタリスク)は「すべての列」を意味するワイルドカードです。
テーブルに何列あるか不明な場合や、
新しいテーブルの内容を素早く確認したい場合に便利です。
製造業での活用例:
- データ移行後に「テーブルの全列が正しく入っているか」を確認する
- 初めて触るテーブルの構造を把握する(探索的分析)
分析・モデル化の考え方
SELECT * は便利ですが、本番環境では原則として避けるべき理由が2つあります。
- パフォーマンス: 不要な列まで読み込むため、データ量が多いテーブルでは遅くなる
- 可読性と保守性: 列が増減した際にクエリの動作が変わり、下流処理でバグが生じる
探索・確認フェーズには SELECT *、報告・分析用クエリには必要な列を明示するのが原則です。
Python で確認する
# No.003:SELECT * — すべての列を取得
# LIMIT を組み合わせて先頭5件を確認する
sql = '''
SELECT *
FROM inspections
LIMIT 5
'''
q(conn, sql)
print()
# テーブルの構造(列名・型)を確認する
print('── テーブル構造の確認 ──────────────────────────')
cursor = conn.execute('PRAGMA table_info(inspections)')
for row in cursor.fetchall():
cid, name, dtype, notnull, default, pk = row
print(f' {cid+1:2d}. {name:20s} {dtype:8s} {"PRIMARY KEY" if pk else "NOT NULL" if notnull else ""}')
── SQL ─────────────────────────────────────────
SELECT *
FROM inspections
LIMIT 5
───────────────────────────────────────────────
shape: (5, 10)
┌─────┬──────────────┬───────────┬───────────┬───┬───────┬──────────────┬────────────┬─────────────┐
│ id ┆ inspection_d ┆ line_code ┆ part_code ┆ … ┆ shift ┆ production_q ┆ defect_qty ┆ inspector_c │
│ --- ┆ ate ┆ --- ┆ --- ┆ ┆ --- ┆ ty ┆ --- ┆ ode │
│ i64 ┆ --- ┆ str ┆ str ┆ ┆ str ┆ --- ┆ i64 ┆ --- │
│ ┆ str ┆ ┆ ┆ ┆ ┆ i64 ┆ ┆ str │
╞═════╪══════════════╪═══════════╪═══════════╪═══╪═══════╪══════════════╪════════════╪═════════════╡
│ 1 ┆ 2024-01-04 ┆ LINE-A1 ┆ ENG-001 ┆ … ┆ 早番 ┆ 450 ┆ 3 ┆ INS-001 │
│ 2 ┆ 2024-01-04 ┆ LINE-A2 ┆ ENG-002 ┆ … ┆ 早番 ┆ 220 ┆ 5 ┆ INS-002 │
│ 3 ┆ 2024-01-04 ┆ LINE-B1 ┆ BRK-001 ┆ … ┆ 早番 ┆ 380 ┆ 2 ┆ INS-003 │
│ 4 ┆ 2024-01-05 ┆ LINE-A1 ┆ ENG-001 ┆ … ┆ 遅番 ┆ 460 ┆ 7 ┆ INS-001 │
│ 5 ┆ 2024-01-05 ┆ LINE-A2 ┆ ENG-002 ┆ … ┆ 遅番 ┆ 215 ┆ 4 ┆ INS-002 │
└─────┴──────────────┴───────────┴───────────┴───┴───────┴──────────────┴────────────┴─────────────┘
↳ 5 行取得
── テーブル構造の確認 ──────────────────────────
1. id INTEGER PRIMARY KEY
2. inspection_date TEXT NOT NULL
3. line_code TEXT NOT NULL
4. part_code TEXT NOT NULL
5. part_name TEXT NOT NULL
6. category TEXT NOT NULL
7. shift TEXT NOT NULL
8. production_qty INTEGER NOT NULL
9. defect_qty INTEGER NOT NULL
10. inspector_code TEXT NOT NULL
結果の読み取り
SELECT *でinspectionsテーブルの 全10列 が返されます
列が多いとPolars の表示が横に広がり、確認しにくいことが分かります
→ これが「本番クエリでは列を絞るべき」理由の一つですPRAGMA table_info(テーブル名)は SQLite 固有のコマンドで、
テーブルの列名・データ型・制約を確認できます
(標準 SQL ではDESCRIBE テーブル名やinformation_schemaを使います)- 今回のテーブルには10列あります
全列を毎回取得するのではなく、用途に応じて3〜5列に絞るだけでも
クエリの意図が格段に伝わりやすくなります
No.004:必要な列だけを取得する
実務での意味
SELECT に取得したい列名を明示することで、必要な情報だけを絞り込めます。
製造業での月次品質報告書では「検査日・ライン・部品名・生産数・不良数」の5列があれば十分で、
「検査員コード」や「カテゴリ」は報告書には不要な場合が多いです。
列を絞ることで3つのメリットがあります:
- データ転送量の削減(特に大規模テーブルでは効果大)
- クエリの意図が明確になる(何のデータを取得したいかが一目で分かる)
- 後続処理の安定性向上(テーブルに列が追加されても結果が変わらない)
分析・モデル化の考え方
列を複数指定する場合は カンマ(,) で区切ります。
可読性のために列を縦に揃えて書くのが推奨スタイルです:
SELECT inspection_date,
line_code,
part_name,
production_qty,
defect_qty
FROM inspections;
Python で確認する
# No.004:必要な列だけを取得
# 月次品質報告書用の5列を抽出する
sql = '''
SELECT inspection_date,
line_code,
part_name,
production_qty,
defect_qty
FROM inspections
LIMIT 10
'''
q(conn, sql)
── SQL ─────────────────────────────────────────
SELECT inspection_date,
line_code,
part_name,
production_qty,
defect_qty
FROM inspections
LIMIT 10
───────────────────────────────────────────────
shape: (10, 5)
┌─────────────────┬───────────┬────────────────────┬────────────────┬────────────┐
│ inspection_date ┆ line_code ┆ part_name ┆ production_qty ┆ defect_qty │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ i64 │
╞═════════════════╪═══════════╪════════════════════╪════════════════╪════════════╡
│ 2024-01-04 ┆ LINE-A1 ┆ ピストンリング ┆ 450 ┆ 3 │
│ 2024-01-04 ┆ LINE-A2 ┆ クランクシャフト ┆ 220 ┆ 5 │
│ 2024-01-04 ┆ LINE-B1 ┆ ブレーキパッド ┆ 380 ┆ 2 │
│ 2024-01-05 ┆ LINE-A1 ┆ ピストンリング ┆ 460 ┆ 7 │
│ 2024-01-05 ┆ LINE-A2 ┆ クランクシャフト ┆ 215 ┆ 4 │
│ 2024-01-05 ┆ LINE-B2 ┆ ブレーキキャリパー ┆ 180 ┆ 6 │
│ 2024-01-08 ┆ LINE-C1 ┆ オルタネータ ┆ 120 ┆ 8 │
│ 2024-01-08 ┆ LINE-B1 ┆ ブレーキパッド ┆ 390 ┆ 4 │
│ 2024-01-09 ┆ LINE-A1 ┆ ピストンリング ┆ 470 ┆ 2 │
│ 2024-01-09 ┆ LINE-C1 ┆ スタータモータ ┆ 100 ┆ 3 │
└─────────────────┴───────────┴────────────────────┴────────────────┴────────────┘
↳ 10 行取得
shape: (10, 5)
| inspection_date | line_code | part_name | production_qty | defect_qty |
|---|---|---|---|---|
| str | str | str | i64 | i64 |
| ”2024-01-04" | "LINE-A1" | "ピストンリング” | 450 | 3 |
| ”2024-01-04" | "LINE-A2" | "クランクシャフト” | 220 | 5 |
| ”2024-01-04" | "LINE-B1" | "ブレーキパッド” | 380 | 2 |
| ”2024-01-05" | "LINE-A1" | "ピストンリング” | 460 | 7 |
| ”2024-01-05" | "LINE-A2" | "クランクシャフト” | 215 | 4 |
| ”2024-01-05" | "LINE-B2" | "ブレーキキャリパー” | 180 | 6 |
| ”2024-01-08" | "LINE-C1" | "オルタネータ” | 120 | 8 |
| ”2024-01-08" | "LINE-B1" | "ブレーキパッド” | 390 | 4 |
| ”2024-01-09" | "LINE-A1" | "ピストンリング” | 470 | 2 |
| ”2024-01-09" | "LINE-C1" | "スタータモータ” | 100 | 3 |
結果の読み取り
SELECT *の10列から 5列 に絞られており、表が格段に読みやすくなっています- 報告書に不要な
category・shift・inspector_codeなどが除外されています production_qty(生産数)とdefect_qty(不良数)を並べることで、
「生産数が多いのに不良が少ない優秀なライン」を直感的に識別できます- 実務では、この5列を Excel にエクスポートして報告書のベースとして使います
SQL でここまで整形しておくと、Excel での作業を最小限にできます
No.005:列に別名を付ける
実務での意味
AS キーワードを使うと、取得した列に 別名(エイリアス) を付けられます。
英語の列名を日本語で表示したり、計算式に分かりやすい名前を付けたりする際に使います。
製造業での活用例:
defect_qty→不良数と表示してExcelに貼り付けるdefect_qty * 1.0 / production_qty * 100→不良率(%)と名付けるinspection_date→検査日と表示して担当者向けレポートを作る
分析・モデル化の考え方
AS の構文:
SELECT 元の列名 AS 別名, ...
FROM テーブル名;
ASは省略可能ですが、省略すると見落としやすいためASを明示するのが推奨 です- 別名にスペースや日本語を使う場合は、DBエンジンによっては引用符で囲む必要があります
SQLite では日本語の別名をそのまま使えます(ダブルクォートで囲むと確実です) - 別名は
ORDER BYで参照できますが、WHERE句では参照できません
(これは実行順序の問題です。No.010 で詳しく説明します)
Python で確認する
# No.005:列に別名を付ける
# 英語の列名を日本語で表示し、計算列にも名前を付ける
sql = '''
SELECT inspection_date AS 検査日,
line_code AS ライン,
part_name AS 部品名,
production_qty AS 生産数,
defect_qty AS 不良数,
ROUND(defect_qty * 100.0 / production_qty, 2) AS 不良率
FROM inspections
LIMIT 8
'''
q(conn, sql)
── SQL ─────────────────────────────────────────
SELECT inspection_date AS 検査日,
line_code AS ライン,
part_name AS 部品名,
production_qty AS 生産数,
defect_qty AS 不良数,
ROUND(defect_qty * 100.0 / production_qty, 2) AS 不良率
FROM inspections
LIMIT 8
───────────────────────────────────────────────
shape: (8, 6)
┌────────────┬─────────┬────────────────────┬────────┬────────┬────────┐
│ 検査日 ┆ ライン ┆ 部品名 ┆ 生産数 ┆ 不良数 ┆ 不良率 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ i64 ┆ f64 │
╞════════════╪═════════╪════════════════════╪════════╪════════╪════════╡
│ 2024-01-04 ┆ LINE-A1 ┆ ピストンリング ┆ 450 ┆ 3 ┆ 0.67 │
│ 2024-01-04 ┆ LINE-A2 ┆ クランクシャフト ┆ 220 ┆ 5 ┆ 2.27 │
│ 2024-01-04 ┆ LINE-B1 ┆ ブレーキパッド ┆ 380 ┆ 2 ┆ 0.53 │
│ 2024-01-05 ┆ LINE-A1 ┆ ピストンリング ┆ 460 ┆ 7 ┆ 1.52 │
│ 2024-01-05 ┆ LINE-A2 ┆ クランクシャフト ┆ 215 ┆ 4 ┆ 1.86 │
│ 2024-01-05 ┆ LINE-B2 ┆ ブレーキキャリパー ┆ 180 ┆ 6 ┆ 3.33 │
│ 2024-01-08 ┆ LINE-C1 ┆ オルタネータ ┆ 120 ┆ 8 ┆ 6.67 │
│ 2024-01-08 ┆ LINE-B1 ┆ ブレーキパッド ┆ 390 ┆ 4 ┆ 1.03 │
└────────────┴─────────┴────────────────────┴────────┴────────┴────────┘
↳ 8 行取得
shape: (8, 6)
| 検査日 | ライン | 部品名 | 生産数 | 不良数 | 不良率 |
|---|---|---|---|---|---|
| str | str | str | i64 | i64 | f64 |
| ”2024-01-04" | "LINE-A1" | "ピストンリング” | 450 | 3 | 0.67 |
| ”2024-01-04" | "LINE-A2" | "クランクシャフト” | 220 | 5 | 2.27 |
| ”2024-01-04" | "LINE-B1" | "ブレーキパッド” | 380 | 2 | 0.53 |
| ”2024-01-05" | "LINE-A1" | "ピストンリング” | 460 | 7 | 1.52 |
| ”2024-01-05" | "LINE-A2" | "クランクシャフト” | 215 | 4 | 1.86 |
| ”2024-01-05" | "LINE-B2" | "ブレーキキャリパー” | 180 | 6 | 3.33 |
| ”2024-01-08" | "LINE-C1" | "オルタネータ” | 120 | 8 | 6.67 |
| ”2024-01-08" | "LINE-B1" | "ブレーキパッド” | 390 | 4 | 1.03 |
結果の読み取り
- 列ヘッダーが日本語に変わり、担当者向けの見やすい表 になりました
ROUND(defect_qty * 100.0 / production_qty, 2) AS 不良率は
計算式(不良率 = 不良数 ÷ 生産数 × 100)に不良率という分かりやすい名前を付けています
ROUND(..., 2)は小数点2桁に丸める関数ですdefect_qty * 100.0 / production_qtyの100.0(浮動小数点)が重要です
100(整数)にすると整数除算になる可能性があるため、
小数点の計算には1.0や100.0を使う のが安全です- このような「日本語見出し + 計算列付き」の SQL を保存しておくと、
毎月同じ形式でデータを取り出せます
No.006:取得件数を制限する
実務での意味
LIMIT 句を使うと、取得する行数の上限を指定できます。
製造業のデータベースでは検査記録が数万〜数十万件規模になることもあり、
全件取得すると処理が重くなります。
実務での典型的な使い方:
- データ確認:
LIMIT 5で先頭5件だけ見て、列の内容を素早く把握する - サンプル取得:
LIMIT 100でサンプルデータを取り出してPythonで統計確認 - ランキング表示:
ORDER BY defect_qty DESC LIMIT 10で不良数ワースト10件
分析・モデル化の考え方
SELECT ...
FROM テーブル名
LIMIT 件数; -- 先頭 N 件
データベースエンジンによる構文の違い:
| DB | 構文 |
|---|---|
| SQLite, MySQL, PostgreSQL | LIMIT n |
| SQL Server | SELECT TOP n ... |
| Oracle, 標準 SQL | FETCH FIRST n ROWS ONLY |
本講座では SQLite を使用するため LIMIT を使います。
Python で確認する
# No.006:取得件数を制限する
# ① LIMIT 3:先頭3件だけ確認
print('=== LIMIT 3 ===')
sql1 = '''
SELECT id, inspection_date, line_code, part_name, defect_qty
FROM inspections
LIMIT 3
'''
q(conn, sql1)
print()
# ② LIMIT 10:先頭10件
print('=== LIMIT 10 ===')
sql2 = '''
SELECT id, inspection_date, line_code, part_name, defect_qty
FROM inspections
LIMIT 10
'''
q(conn, sql2)
print()
# ③ LIMIT なし:全件取得(件数だけ確認)
total = conn.execute('SELECT COUNT(*) FROM inspections').fetchone()[0]
print(f'LIMIT なし(全件): {total} 件')
=== LIMIT 3 ===
── SQL ─────────────────────────────────────────
SELECT id, inspection_date, line_code, part_name, defect_qty
FROM inspections
LIMIT 3
───────────────────────────────────────────────
shape: (3, 5)
┌─────┬─────────────────┬───────────┬──────────────────┬────────────┐
│ id ┆ inspection_date ┆ line_code ┆ part_name ┆ defect_qty │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ str ┆ i64 │
╞═════╪═════════════════╪═══════════╪══════════════════╪════════════╡
│ 1 ┆ 2024-01-04 ┆ LINE-A1 ┆ ピストンリング ┆ 3 │
│ 2 ┆ 2024-01-04 ┆ LINE-A2 ┆ クランクシャフト ┆ 5 │
│ 3 ┆ 2024-01-04 ┆ LINE-B1 ┆ ブレーキパッド ┆ 2 │
└─────┴─────────────────┴───────────┴──────────────────┴────────────┘
↳ 3 行取得
=== LIMIT 10 ===
── SQL ─────────────────────────────────────────
SELECT id, inspection_date, line_code, part_name, defect_qty
FROM inspections
LIMIT 10
───────────────────────────────────────────────
shape: (10, 5)
┌─────┬─────────────────┬───────────┬────────────────────┬────────────┐
│ id ┆ inspection_date ┆ line_code ┆ part_name ┆ defect_qty │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ str ┆ i64 │
╞═════╪═════════════════╪═══════════╪════════════════════╪════════════╡
│ 1 ┆ 2024-01-04 ┆ LINE-A1 ┆ ピストンリング ┆ 3 │
│ 2 ┆ 2024-01-04 ┆ LINE-A2 ┆ クランクシャフト ┆ 5 │
│ 3 ┆ 2024-01-04 ┆ LINE-B1 ┆ ブレーキパッド ┆ 2 │
│ 4 ┆ 2024-01-05 ┆ LINE-A1 ┆ ピストンリング ┆ 7 │
│ 5 ┆ 2024-01-05 ┆ LINE-A2 ┆ クランクシャフト ┆ 4 │
│ 6 ┆ 2024-01-05 ┆ LINE-B2 ┆ ブレーキキャリパー ┆ 6 │
│ 7 ┆ 2024-01-08 ┆ LINE-C1 ┆ オルタネータ ┆ 8 │
│ 8 ┆ 2024-01-08 ┆ LINE-B1 ┆ ブレーキパッド ┆ 4 │
│ 9 ┆ 2024-01-09 ┆ LINE-A1 ┆ ピストンリング ┆ 2 │
│ 10 ┆ 2024-01-09 ┆ LINE-C1 ┆ スタータモータ ┆ 3 │
└─────┴─────────────────┴───────────┴────────────────────┴────────────┘
↳ 10 行取得
LIMIT なし(全件): 30 件
結果の読み取り
LIMIT 3で先頭3件、LIMIT 10で先頭10件が返されますLIMITがなければテーブルの全30件が取得されます
本番テーブルが数万件の場合にLIMITなしで SELECT するとシステム負荷が高まりますCOUNT(*)は件数を数える集計関数です(第3章 No.021 で詳しく学びます)
ここでは「全件数を確認するための関数」として使っていますLIMITとORDER BYを組み合わせると「ワーストN件」「トップN件」を
効率的に取得できます(No.007/008 で組み合わせて使います)
No.007:ORDER BYで並び替える
実務での意味
ORDER BY 句を使うと、指定した列の値の順番 でレコードを並び替えられます。
品質管理では「不良数が多いラインを先頭に表示したい」「検査日の古い順から確認したい」
といった場面が頻繁にあります。
製造業での活用例:
ORDER BY defect_qty→ 不良数が少ない順(昇順)にランキングORDER BY inspection_date→ 検査日の古い順に時系列で確認ORDER BY line_code, inspection_date→ ライン別・日付順に整理
分析・モデル化の考え方
ORDER BY の構文:
SELECT ...
FROM テーブル名
ORDER BY 列名; -- デフォルトは昇順(小さい値が先頭)
- デフォルトは 昇順(ASC: Ascending) — 数値は小さい順、文字列はアルファベット順
- 降順にしたい場合は
DESCを付ける(No.008 で詳しく扱います) - 複数列での並び替え:
ORDER BY 列1, 列2— 列1で並べ、同値の場合は列2で並べる ORDER BYはWHERE・GROUP BYの後に書きますが、
実行順序はSELECTの直後です(No.010 で解説)
Python で確認する
# No.007:ORDER BY — 不良数の昇順で並べる(小さい順)
sql = '''
SELECT inspection_date AS 検査日,
line_code AS ライン,
part_name AS 部品名,
defect_qty AS 不良数
FROM inspections
ORDER BY defect_qty
LIMIT 10
'''
q(conn, sql)
── SQL ─────────────────────────────────────────
SELECT inspection_date AS 検査日,
line_code AS ライン,
part_name AS 部品名,
defect_qty AS 不良数
FROM inspections
ORDER BY defect_qty
LIMIT 10
───────────────────────────────────────────────
shape: (10, 4)
┌────────────┬─────────┬──────────────────┬────────┐
│ 検査日 ┆ ライン ┆ 部品名 ┆ 不良数 │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 │
╞════════════╪═════════╪══════════════════╪════════╡
│ 2024-01-04 ┆ LINE-B1 ┆ ブレーキパッド ┆ 2 │
│ 2024-01-09 ┆ LINE-A1 ┆ ピストンリング ┆ 2 │
│ 2024-01-22 ┆ LINE-A1 ┆ ピストンリング ┆ 2 │
│ 2024-01-23 ┆ LINE-B2 ┆ ブレーキパッド ┆ 2 │
│ 2024-01-04 ┆ LINE-A1 ┆ ピストンリング ┆ 3 │
│ 2024-01-09 ┆ LINE-C1 ┆ スタータモータ ┆ 3 │
│ 2024-01-15 ┆ LINE-A2 ┆ クランクシャフト ┆ 3 │
│ 2024-01-17 ┆ LINE-B1 ┆ ブレーキパッド ┆ 3 │
│ 2024-01-05 ┆ LINE-A2 ┆ クランクシャフト ┆ 4 │
│ 2024-01-08 ┆ LINE-B1 ┆ ブレーキパッド ┆ 4 │
└────────────┴─────────┴──────────────────┴────────┘
↳ 10 行取得
shape: (10, 4)
| 検査日 | ライン | 部品名 | 不良数 |
|---|---|---|---|
| str | str | str | i64 |
| ”2024-01-04" | "LINE-B1" | "ブレーキパッド” | 2 |
| ”2024-01-09" | "LINE-A1" | "ピストンリング” | 2 |
| ”2024-01-22" | "LINE-A1" | "ピストンリング” | 2 |
| ”2024-01-23" | "LINE-B2" | "ブレーキパッド” | 2 |
| ”2024-01-04" | "LINE-A1" | "ピストンリング” | 3 |
| ”2024-01-09" | "LINE-C1" | "スタータモータ” | 3 |
| ”2024-01-15" | "LINE-A2" | "クランクシャフト” | 3 |
| ”2024-01-17" | "LINE-B1" | "ブレーキパッド” | 3 |
| ”2024-01-05" | "LINE-A2" | "クランクシャフト” | 4 |
| ”2024-01-08" | "LINE-B1" | "ブレーキパッド” | 4 |
結果の読み取り
ORDER BY defect_qtyで 不良数が少ない順(昇順) に並び替えられています
デフォルトの並び替え方向は昇順(ASC)です- 先頭に不良数2件のレコードが複数表示されています
同じ不良数のレコード内での順序は保証されないことに注意が必要です
→ 日付も含めてORDER BY defect_qty, inspection_dateとすると順序が安定します - 「不良数が最も少ないライン・部品」を一目で把握できるため、
「優秀ラインのベストプラクティスを他ラインに横展開する」という分析に使えます
No.008:昇順・降順を指定する
実務での意味
ORDER BY の後に ASC(昇順)または DESC(降順)を付けることで、
並び替えの方向を明示的に指定できます。
製造業での使い分け:
ORDER BY defect_qty DESC→ 不良数ワースト順(問題ラインを特定)ORDER BY defect_qty ASC→ 不良数ベスト順(改善ラインのお手本を特定)ORDER BY inspection_date DESC→ 最新の検査記録から確認
分析・モデル化の考え方
ORDER BY 列名 ASC -- 昇順(デフォルト): 小 → 大
ORDER BY 列名 DESC -- 降順: 大 → 小
複数列の組み合わせ:
ORDER BY line_code ASC, defect_qty DESC
→ まずラインコードで昇順、同じライン内では不良数の多い順に並べる。
これにより「ライン別のワースト記録」を一覧できます。
Python で確認する
# No.008:降順・昇順を使い分ける
# ① 降順(DESC):不良数ワースト順
print('=== ORDER BY defect_qty DESC(ワースト順) ===')
sql_desc = '''
SELECT inspection_date AS 検査日,
line_code AS ライン,
part_name AS 部品名,
shift AS シフト,
defect_qty AS 不良数
FROM inspections
ORDER BY defect_qty DESC
LIMIT 8
'''
q(conn, sql_desc)
print()
# ② 複合 ORDER BY:ライン昇順 + 不良数降順
print('=== ORDER BY line_code ASC, defect_qty DESC(ライン別ワースト) ===')
sql_combo = '''
SELECT line_code AS ライン,
part_name AS 部品名,
shift AS シフト,
defect_qty AS 不良数
FROM inspections
ORDER BY line_code ASC, defect_qty DESC
LIMIT 10
'''
q(conn, sql_combo)
print()
# ③ 棒グラフ:ORDER BY DESC の結果を可視化
rows = conn.execute('''
SELECT line_code || '-' || part_code AS label,
defect_qty
FROM inspections
ORDER BY defect_qty DESC
LIMIT 10
''').fetchall()
labels = [r[0] for r in rows]
values = [r[1] for r in rows]
fig, ax = plt.subplots(figsize=(10, 5))
colors = ['#D65F5F' if v >= 10 else '#4878CF' for v in values]
bars = ax.barh(list(reversed(labels)), list(reversed(values)),
color=list(reversed(colors)), alpha=0.85, edgecolor='black', linewidth=0.4)
for bar in bars:
ax.text(bar.get_width() + 0.1, bar.get_y() + bar.get_height()/2,
f'{int(bar.get_width())}', va='center', fontsize=9)
ax.set_title('ORDER BY defect_qty DESC — 不良数ワースト10(ライン×部品)', fontsize=12, pad=10)
ax.set_xlabel('不良数(個)', fontsize=10)
ax.set_ylabel('ライン-部品コード', fontsize=10)
ax.grid(axis='x', alpha=0.3)
ax.axvline(x=10, color='#D65F5F', linestyle='--', linewidth=1, alpha=0.6, label='不良数=10 の閾値')
ax.legend(fontsize=9)
plt.tight_layout()
plt.savefig('no008_order_by_desc.svg', format='svg', bbox_inches='tight')
plt.show()
print('保存完了: no008_order_by_desc.svg')
=== ORDER BY defect_qty DESC(ワースト順) ===
── SQL ─────────────────────────────────────────
SELECT inspection_date AS 検査日,
line_code AS ライン,
part_name AS 部品名,
shift AS シフト,
defect_qty AS 不良数
FROM inspections
ORDER BY defect_qty DESC
LIMIT 8
───────────────────────────────────────────────
shape: (8, 5)
┌────────────┬─────────┬────────────────────┬────────┬────────┐
│ 検査日 ┆ ライン ┆ 部品名 ┆ シフト ┆ 不良数 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ str ┆ i64 │
╞════════════╪═════════╪════════════════════╪════════╪════════╡
│ 2024-01-16 ┆ LINE-A1 ┆ ピストンリング ┆ 夜間 ┆ 15 │
│ 2024-01-11 ┆ LINE-B1 ┆ ブレーキパッド ┆ 夜間 ┆ 12 │
│ 2024-01-22 ┆ LINE-B1 ┆ ショックアブソーバ ┆ 遅番 ┆ 11 │
│ 2024-01-10 ┆ LINE-A2 ┆ ショックアブソーバ ┆ 遅番 ┆ 9 │
│ 2024-01-18 ┆ LINE-B2 ┆ ブレーキキャリパー ┆ 夜間 ┆ 9 │
│ 2024-01-08 ┆ LINE-C1 ┆ オルタネータ ┆ 夜間 ┆ 8 │
│ 2024-01-05 ┆ LINE-A1 ┆ ピストンリング ┆ 遅番 ┆ 7 │
│ 2024-01-15 ┆ LINE-B2 ┆ ショックアブソーバ ┆ 遅番 ┆ 7 │
└────────────┴─────────┴────────────────────┴────────┴────────┘
↳ 8 行取得
=== ORDER BY line_code ASC, defect_qty DESC(ライン別ワースト) ===
── SQL ─────────────────────────────────────────
SELECT line_code AS ライン,
part_name AS 部品名,
shift AS シフト,
defect_qty AS 不良数
FROM inspections
ORDER BY line_code ASC, defect_qty DESC
LIMIT 10
───────────────────────────────────────────────
shape: (10, 4)
┌─────────┬────────────────────┬────────┬────────┐
│ ライン ┆ 部品名 ┆ シフト ┆ 不良数 │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 │
╞═════════╪════════════════════╪════════╪════════╡
│ LINE-A1 ┆ ピストンリング ┆ 夜間 ┆ 15 │
│ LINE-A1 ┆ ピストンリング ┆ 遅番 ┆ 7 │
│ LINE-A1 ┆ ピストンリング ┆ 遅番 ┆ 5 │
│ LINE-A1 ┆ ピストンリング ┆ 早番 ┆ 4 │
│ LINE-A1 ┆ ピストンリング ┆ 早番 ┆ 3 │
│ LINE-A1 ┆ ピストンリング ┆ 早番 ┆ 2 │
│ LINE-A1 ┆ ピストンリング ┆ 早番 ┆ 2 │
│ LINE-A2 ┆ ショックアブソーバ ┆ 遅番 ┆ 9 │
│ LINE-A2 ┆ クランクシャフト ┆ 早番 ┆ 6 │
│ LINE-A2 ┆ クランクシャフト ┆ 早番 ┆ 5 │
└─────────┴────────────────────┴────────┴────────┘
↳ 10 行取得
保存完了: no008_order_by_desc.svg
結果の読み取り
- DESC(降順) で不良数が多い順に並び、ワースト記録が先頭に来ます
最大不良数はLINE-A1 / ピストンリング / 夜間の15件 — 夜間シフトに着目すべきポイントです - 複合 ORDER BY(
line_code ASC, defect_qty DESC)で
ライン別のワースト記録を整理できます
→ LINE-A1 の夜間シフトに不良が集中していることが読み取れます - グラフ(横棒グラフ) で ORDER BY の結果を視覚化しました
赤い棒(不良数≥10)が LINE-A1 と LINE-B1 に集中しており、
これらが優先的に改善すべきラインです - 実務では「月次ワーストランキング」をこの SQL で毎月自動取得し、
品質会議の資料に使うことができます
No.009:DISTINCTで重複を除外する
実務での意味
SELECT DISTINCT は、重複する値を除いてユニークな値だけ を取得します。
製造業のデータベースでは「このテーブルに何種類の部品が登録されているか」
「検査対象のラインは何ラインあるか」といった確認によく使います。
製造業での活用例:
SELECT DISTINCT part_code→ 検査対象の部品コード一覧を取得SELECT DISTINCT line_code→ 稼働中のライン一覧を確認SELECT DISTINCT line_code, shift→ ライン×シフトの組み合わせを把握
分析・モデル化の考え方
SELECT DISTINCT 列名
FROM テーブル名;
DISTINCTはSELECTの直後に配置します- 複数列の DISTINCT:
SELECT DISTINCT 列1, 列2の場合、
「列1と列2の組み合わせ」がユニークなレコードだけが返されます DISTINCTは全データを読み込んでから重複を排除するため、
件数が多いテーブルでは計算コストが高くなります
DISTINCT で得られるユニーク値の集合は
マスタテーブル(parts_master 等)との一致確認 にも使えます。
「検査記録に出てくる部品コードがすべてマスタに登録されているか」を
確認する際の出発点となります(第5章 JOIN で詳しく扱います)。
Python で確認する
# No.009:DISTINCT — 重複を除いてユニークな値だけ取得
# ① 検査対象の部品コード(重複除去)
print('=== SELECT DISTINCT part_code ===')
sql1 = '''
SELECT DISTINCT part_code
FROM inspections
ORDER BY part_code
'''
q(conn, sql1)
print()
# ② ラインの一覧(重複除去)
print('=== SELECT DISTINCT line_code ===')
sql2 = '''
SELECT DISTINCT line_code
FROM inspections
ORDER BY line_code
'''
q(conn, sql2)
print()
# ③ ライン × シフトの組み合わせ(複数列の DISTINCT)
print('=== SELECT DISTINCT line_code, shift ===')
sql3 = '''
SELECT DISTINCT line_code, shift
FROM inspections
ORDER BY line_code, shift
'''
q(conn, sql3)
print()
# DISTINCT あり vs なし の件数比較
total = conn.execute('SELECT COUNT(part_code) FROM inspections').fetchone()[0]
distinct = conn.execute('SELECT COUNT(DISTINCT part_code) FROM inspections').fetchone()[0]
print(f'part_code の全件数: {total} 件 → DISTINCT 後: {distinct} 件')
=== SELECT DISTINCT part_code ===
── SQL ─────────────────────────────────────────
SELECT DISTINCT part_code
FROM inspections
ORDER BY part_code
───────────────────────────────────────────────
shape: (7, 1)
┌───────────┐
│ part_code │
│ --- │
│ str │
╞═══════════╡
│ BRK-001 │
│ BRK-002 │
│ ELC-001 │
│ ELC-002 │
│ ENG-001 │
│ ENG-002 │
│ SUS-001 │
└───────────┘
↳ 7 行取得
=== SELECT DISTINCT line_code ===
── SQL ─────────────────────────────────────────
SELECT DISTINCT line_code
FROM inspections
ORDER BY line_code
───────────────────────────────────────────────
shape: (5, 1)
┌───────────┐
│ line_code │
│ --- │
│ str │
╞═══════════╡
│ LINE-A1 │
│ LINE-A2 │
│ LINE-B1 │
│ LINE-B2 │
│ LINE-C1 │
└───────────┘
↳ 5 行取得
=== SELECT DISTINCT line_code, shift ===
── SQL ─────────────────────────────────────────
SELECT DISTINCT line_code, shift
FROM inspections
ORDER BY line_code, shift
───────────────────────────────────────────────
shape: (14, 2)
┌───────────┬───────┐
│ line_code ┆ shift │
│ --- ┆ --- │
│ str ┆ str │
╞═══════════╪═══════╡
│ LINE-A1 ┆ 夜間 │
│ LINE-A1 ┆ 早番 │
│ LINE-A1 ┆ 遅番 │
│ LINE-A2 ┆ 早番 │
│ LINE-A2 ┆ 遅番 │
│ … ┆ … │
│ LINE-B2 ┆ 早番 │
│ LINE-B2 ┆ 遅番 │
│ LINE-C1 ┆ 夜間 │
│ LINE-C1 ┆ 早番 │
│ LINE-C1 ┆ 遅番 │
└───────────┴───────┘
↳ 14 行取得
part_code の全件数: 30 件 → DISTINCT 後: 7 件
結果の読み取り
SELECT DISTINCT part_codeで 7種類の部品コード が確認できます
(30件の検査記録のうち、部品コードは7種類 = テーブルに7行 × 複数回登録)- ライン一覧(
DISTINCT line_code)から 5ライン が稼働中であることが分かります DISTINCT line_code, shiftの組み合わせから、
各ラインで行われているシフト構成が確認できます
→ 一部のラインが夜間シフトを実施していないことが読み取れますCOUNT(part_code)= 30件(重複あり)、COUNT(DISTINCT part_code)= 7件
この差が「1部品あたり何回検査されたか」の平均(30÷7 ≈ 4.3回/部品)を示しています
No.010:SQLの実行順序を理解する
実務での意味
SQL のバグの多くは「書く順序と実行される順序の違い」から生じます。
例えば「SELECT で付けた別名を WHERE で使おうとしてエラーになる」という
初学者によくあるミスは、この実行順序を理解することで防げます。
分析・モデル化の考え方
SQL の 書く順序:
① SELECT 部品名, 不良率 -- 取得する列
② FROM inspections -- テーブル指定
③ WHERE 不良率 >= 1.0 -- ← エラー!(まだ不良率は存在しない)
④ ORDER BY 不良率 DESC
⑤ LIMIT 5
SQL の 実行される順序(DB エンジンの処理順):
| 実行順 | 句 | 処理内容 |
|---|---|---|
| 1 | FROM | テーブルを読み込む |
| 2 | WHERE | 行の絞り込み |
| 3 | GROUP BY | グループ集計 |
| 4 | HAVING | 集計結果の絞り込み |
| 5 | SELECT | 列の計算・別名の定義 |
| 6 | ORDER BY | 並び替え |
| 7 | LIMIT | 件数制限 |
WHERE は SELECT(第5番)よりも先に実行(第2番)されるため、
SELECT で定義した別名は WHERE では使えません。
一方、ORDER BY は SELECT の後なので、SELECT の別名を参照できます。
Python で確認する
# No.010:実行順序の理解
# ① SELECT の別名を ORDER BY で使う(OK — ORDER BY は SELECT の後に実行される)
print('=== SELECT 別名を ORDER BY で使う(OK) ===')
sql_ok = '''
SELECT part_name AS 部品名,
defect_qty * 100.0 / production_qty AS 不良率,
production_qty AS 生産数
FROM inspections
ORDER BY 不良率 DESC
LIMIT 5
'''
q(conn, sql_ok)
print()
# ② SELECT の別名を WHERE で使うとエラー(WHERE は SELECT より前に実行)
print('=== SELECT 別名を WHERE で使うとエラーになる例 ===')
try:
sql_ng = '''
SELECT part_name AS 部品名,
defect_qty * 100.0 / production_qty AS 不良率
FROM inspections
WHERE 不良率 >= 2.0
'''
conn.execute(sql_ng).fetchall()
except Exception as e:
print(f' エラー発生: {e}')
print()
print(' ↓ 正しい書き方: WHERE では元の計算式を使う')
# ③ 正しい書き方:WHERE では元の列名/計算式を使う
print()
print('=== 正しい書き方: WHERE では計算式を直接書く ===')
sql_correct = '''
SELECT part_name AS 部品名,
defect_qty * 100.0 / production_qty AS 不良率,
production_qty AS 生産数
FROM inspections
WHERE defect_qty * 1.0 / production_qty >= 0.02
ORDER BY 不良率 DESC
'''
q(conn, sql_correct)
=== SELECT 別名を ORDER BY で使う(OK) ===
── SQL ─────────────────────────────────────────
SELECT part_name AS 部品名,
defect_qty * 100.0 / production_qty AS 不良率,
production_qty AS 生産数
FROM inspections
ORDER BY 不良率 DESC
LIMIT 5
───────────────────────────────────────────────
shape: (5, 3)
┌────────────────────┬──────────┬────────┐
│ 部品名 ┆ 不良率 ┆ 生産数 │
│ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ i64 │
╞════════════════════╪══════════╪════════╡
│ ショックアブソーバ ┆ 12.5 ┆ 88 │
│ ショックアブソーバ ┆ 10.0 ┆ 90 │
│ ショックアブソーバ ┆ 8.235294 ┆ 85 │
│ スタータモータ ┆ 7.142857 ┆ 98 │
│ オルタネータ ┆ 6.666667 ┆ 120 │
└────────────────────┴──────────┴────────┘
↳ 5 行取得
=== SELECT 別名を WHERE で使うとエラーになる例 ===
=== 正しい書き方: WHERE では計算式を直接書く ===
── SQL ─────────────────────────────────────────
SELECT part_name AS 部品名,
defect_qty * 100.0 / production_qty AS 不良率,
production_qty AS 生産数
FROM inspections
WHERE defect_qty * 1.0 / production_qty >= 0.02
ORDER BY 不良率 DESC
───────────────────────────────────────────────
shape: (17, 3)
┌────────────────────┬──────────┬────────┐
│ 部品名 ┆ 不良率 ┆ 生産数 │
│ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ i64 │
╞════════════════════╪══════════╪════════╡
│ ショックアブソーバ ┆ 12.5 ┆ 88 │
│ ショックアブソーバ ┆ 10.0 ┆ 90 │
│ ショックアブソーバ ┆ 8.235294 ┆ 85 │
│ スタータモータ ┆ 7.142857 ┆ 98 │
│ オルタネータ ┆ 6.666667 ┆ 120 │
│ … ┆ … ┆ … │
│ スタータモータ ┆ 3.0 ┆ 100 │
│ ブレーキキャリパー ┆ 2.857143 ┆ 175 │
│ クランクシャフト ┆ 2.608696 ┆ 230 │
│ ブレーキキャリパー ┆ 2.352941 ┆ 170 │
│ クランクシャフト ┆ 2.272727 ┆ 220 │
└────────────────────┴──────────┴────────┘
↳ 17 行取得
shape: (17, 3)
| 部品名 | 不良率 | 生産数 |
|---|---|---|
| str | f64 | i64 |
| ”ショックアブソーバ” | 12.5 | 88 |
| ”ショックアブソーバ” | 10.0 | 90 |
| ”ショックアブソーバ” | 8.235294 | 85 |
| ”スタータモータ” | 7.142857 | 98 |
| ”オルタネータ” | 6.666667 | 120 |
| … | … | … |
| ”スタータモータ” | 3.0 | 100 |
| ”ブレーキキャリパー” | 2.857143 | 175 |
| ”クランクシャフト” | 2.608696 | 230 |
| ”ブレーキキャリパー” | 2.352941 | 170 |
| ”クランクシャフト” | 2.272727 | 220 |
# No.010:SQL実行順序の図解(matplotlib)
fig, axes = plt.subplots(1, 2, figsize=(11, 7))
clauses_write = [
('① SELECT', '#4878CF'),
('② FROM', '#6ACC65'),
('③ WHERE', '#D65F5F'),
('④ GROUP BY', '#B47CC7'),
('⑤ HAVING', '#C4AD66'),
('⑥ ORDER BY', '#88BEAA'),
('⑦ LIMIT', '#E07050'),
]
clauses_exec = [
('① FROM', '#6ACC65'),
('② WHERE', '#D65F5F'),
('③ GROUP BY', '#B47CC7'),
('④ HAVING', '#C4AD66'),
('⑤ SELECT', '#4878CF'),
('⑥ ORDER BY', '#88BEAA'),
('⑦ LIMIT', '#E07050'),
]
titles = ['書く順序(SQL の構文)', '実行される順序(DB エンジン)']
all_clauses = [clauses_write, clauses_exec]
for ax, title, clauses in zip(axes, titles, all_clauses):
n = len(clauses)
ax.set_xlim(0, 1)
ax.set_ylim(-0.5, n - 0.5)
ax.axis('off')
ax.set_title(title, fontsize=12, fontweight='bold', pad=15)
for i, (label, color) in enumerate(clauses):
y = n - 1 - i
ax.barh(y, 0.75, left=0.125, color=color, alpha=0.78, height=0.6,
edgecolor='white', linewidth=1.5)
ax.text(0.5, y, label, ha='center', va='center',
fontsize=12, fontweight='bold', color='white')
# 下向き矢印(最後の要素以外)
if i < n - 1:
ax.annotate('', xy=(0.5, y - 0.38), xytext=(0.5, y - 0.32),
arrowprops=dict(arrowstyle='->', color='#555555', lw=1.5))
# SELECT と FROM の順序が入れ替わることをアノテーション
axes[0].text(0.98, 6, '← SQL はここから書き始める', va='center', ha='right',
fontsize=9, color='#4878CF', style='italic')
axes[1].text(0.98, 6, '← DB はここから処理を始める', va='center', ha='right',
fontsize=9, color='#6ACC65', style='italic')
axes[1].text(0.98, 2, '← SELECT はここで初めて評価される', va='center', ha='right',
fontsize=8, color='#4878CF', style='italic')
plt.suptitle('SQL の書く順序と実行順序の違い', fontsize=14, fontweight='bold', y=1.02)
plt.tight_layout()
plt.savefig('no010_sql_execution_order.svg', format='svg', bbox_inches='tight')
plt.show()
print('保存完了: no010_sql_execution_order.svg')
findfont: Failed to find font weight bold, now using 400.
findfont: Failed to find font weight bold, now using 400.
保存完了: no010_sql_execution_order.svg
結果の読み取り
-
① OK の例(
ORDER BY 不良率 DESC):
SELECTで不良率という別名を定義してからORDER BYで使えます
→ 実行順序5番(SELECT)→6番(ORDER BY)なので参照可能です
結果から不良率が2%以上の記録が上位に表示されています -
② エラーの例(
WHERE 不良率 >= 2.0):
WHEREは実行順序2番で処理されますが、別名不良率は5番のSELECTで定義されるため
「そんな列は存在しない」というエラーになります
→ WHERE では元の列名 or 計算式を直接書く のが正しい書き方です -
③ 正しい書き方(
WHERE defect_qty * 1.0 / production_qty >= 0.02):
WHERE内で計算式を直接記述することで正しく動作します
不良率2%以上(0.02)の記録だけが絞り込まれ、降順で表示されます -
図解: 書く順序と実行順序を並べると、
SELECTとFROMの位置が入れ替わり、
SELECTが実際には5番目に評価されることが視覚的に確認できます
この図を頭に入れておくと、SQL のバグを事前に防げます
対象ノックを通して見える実務上の示唆
No.001〜010 の第1章を通して、製造業データ分析における以下の示唆が得られます。
1. SQL はデータ分析の「第一の道具」
品質管理データの分析において、SQL は最初の入り口です。
Excel で手作業していた「並び替え・列絞り込み・重複除去」は、
SQL なら再実行可能なクエリとして保存でき、毎月0秒で再現できます。
2. WHERE より前に LIMIT を使ってデータを確認する習慣
大量データを扱う際は、まず LIMIT 10 で先頭を確認し、
データの型・NULL の有無・値の範囲を把握してから分析を始める習慣が重要です。
本番環境で誤って巨大な取得クエリを走らせると、システムに影響が出ます。
3. ORDER BY は必ず明示する
「並び順はどうせ変わらない」という思い込みは危険です。
テーブルにレコードが追加・削除されたり、DB のバージョンが変わったりすると
ORDER BY なしのクエリは異なる順序で返ってきます。
再現性のある分析には ORDER BY を必ず指定するのが原則です。
4. SELECT * は探索専用、報告書クエリでは列を明示する
SELECT * は「何がテーブルに入っているか確認する」探索フェーズ専用です。
月次品質報告書・BI ダッシュボード・自動化スクリプトには
必ず 必要な列を明示した SELECT を使用してください。
5. 書く順序と実行順序の違いを常に意識する
SQL のバグの多くは「実行順序の誤解」から生じます。
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
この実行順序を頭に入れておくことで、エラーの原因を素早く特定できます。
実務導入する場合に必要なこと
SQL を製造現場のデータ分析に導入するためのチェックリスト
1. データベース環境の整備
- 社内の品質管理システムが使っている DB エンジンを確認(MySQL / Oracle / SQL Server / PostgreSQL 等)
- SELECT 権限のある読み取り専用アカウントを取得する(誤操作によるデータ破壊を防ぐ)
- DB の接続情報(ホスト・ポート・DB名・認証情報)を安全に管理する
2. データ構造の把握
- 利用するテーブルの列名・型・制約を
PRAGMA table_info等で確認する - NULL が入りうる列を把握し、NULL の扱いを理解する(第2章 No.020 で扱います)
- テーブル間の関係(外部キー)を把握して JOIN の準備をする(第5章 No.041〜050)
3. クエリの管理
- よく使うクエリを
.sqlファイルとして保存する(バージョン管理に Git を使うと更に良い) - クエリにコメント(
-- コメント)を付けて意図を記録する - 定期実行が必要なクエリは Python から
sqlite3やsqlalchemyで自動実行する
4. 品質管理部門への展開
- 本シリーズ(No.001〜100)を社内 SQL 研修テキストとして活用する
- 月次品質報告書用のクエリを整備して担当者全員が使えるようにする
- SQL の結果を matplotlib / Polars で可視化するパイプラインを構築する
まとめ
本章(No.001〜010)では、SQL の基本構文を自動車部品メーカーの検査記録DBを題材に学びました。
| No. | 学んだこと | 実務での使いどころ |
|---|---|---|
| 001 | SELECT文の基本 | DBから最初のデータ取得の第一歩 |
| 002 | FROM句でテーブル指定 | 検査記録 / 部品マスタを使い分ける |
| 003 | SELECT *で全列取得 | 新規テーブルの構造を素早く把握 |
| 004 | 必要な列だけ取得 | 報告書用データを最小限に絞り込む |
| 005 | 列に別名を付ける | 英語列名を日本語で出力・計算列に名前 |
| 006 | LIMITで件数制限 | 大量データをN件だけサンプル確認 |
| 007 | ORDER BYで並べ替え | 不良数の少ない順・多い順でランキング |
| 008 | 昇順・降順の指定 | ワーストN件・ベストN件を瞬時に特定 |
| 009 | DISTINCTで重複除外 | 部品コード・ライン種類を一覧で把握 |
| 010 | SQL実行順序の理解 | WHERE/ORDER BYのエラーの原因把握 |
次の章(第2章:条件指定と絞り込み No.011〜020) では、
WHERE 句による行のフィルタリングを学びます。
「不良数が10件以上のラインだけ抽出する」「特定ラインの記録だけ見る」
といった実務の核心操作を習得します。
法人向けのご相談
製造業の DX 推進・データ分析基盤の構築・社内 SQL / Python 研修については、
数理工房にお気軽にご相談ください。
📩 お問い合わせ: surikobo.co.jp/contact
まずはお気軽にご相談ください。
本記事は「データ分析のための SQL 入門 100本ノック」シリーズの第1章です。
シリーズ全体の目次は こちら からご確認ください。