100本ノック / SQL / データ分析のためのSQL入門100本ノック

検査記録DBからSQLで不良データを抽出・分析する

検査記録DBからSQLで不良データを抽出・分析する

SQL 100本ノック 第1章(No.001〜No.010):SQLの基本

本記事は「データ分析のための SQL 入門 100本ノック」シリーズの 第1章 です。
SQL の最も基本的な命令である SELECT 文から始め、
FROMLIMITORDER BYDISTINCT など、
データ取得の基礎を製造業の品質管理データを通して体系的に学びます。

[!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.タイトル製造業での活用場面
001SELECT文の基本を理解するDBから最初のデータ取得
002FROM句でテーブルを指定する複数テーブル(検査記録 / 部品マスタ)の使い分け
003すべての列を取得するデータ構造の素早い確認(探索的分析)
004必要な列だけを取得する報告書用データの列絞り込み
005列に別名を付ける英語列名を日本語ラベルで出力
006取得件数を制限する大量データの先頭N件確認
007ORDER BYで並び替える不良数の多い工程を先頭に表示
008昇順・降順を指定するワースト / ベスト順の切り替え
009DISTINCTで重複を除外する検査対象の部品コード種類を把握
010SQLの実行順序を理解するバグの原因となる「書き順≠実行順」の把握

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')

svg

データ概要グラフ保存完了: no_overview_line_stats.svg

No.001:SELECT文の基本を理解する

実務での意味

SELECT 文は SQL でデータを取得するための基本命令です。
品質管理DBから「どの部品が不良数が多いか」「今月の検査記録を一覧で確認したい」
といった分析はすべて SELECT 文から始まります。

製造業での活用例:

  • 検査記録から 部品名・不良数 だけを抽出して月次報告に使う
  • 特定ラインの記録を取り出して品質会議の資料にする

分析・モデル化の考え方

SELECT 文の最小構成:

SELECT 列名1, 列名2, ...
FROM   テーブル名;
  • SELECT : 取得したい列(複数の場合はカンマ区切り)
  • FROM : データの取得元テーブル
  • SQL 文はセミコロン(;)で終わりますが、Python の sqlite3 では省略可能です
  • SQL キーワード(SELECTFROM)は大文字・小文字どちらでも動作しますが、
    慣習として 大文字 で書くことで 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_nameshiftdefect_qty
strstri64
”ピストンリング""早番”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_codepart_namecategoryunit_pricesupplier
strstrstri64str
”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つあります。

  1. パフォーマンス: 不要な列まで読み込むため、データ量が多いテーブルでは遅くなる
  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つのメリットがあります:

  1. データ転送量の削減(特に大規模テーブルでは効果大)
  2. クエリの意図が明確になる(何のデータを取得したいかが一目で分かる)
  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_dateline_codepart_nameproduction_qtydefect_qty
strstrstri64i64
”2024-01-04""LINE-A1""ピストンリング”4503
”2024-01-04""LINE-A2""クランクシャフト”2205
”2024-01-04""LINE-B1""ブレーキパッド”3802
”2024-01-05""LINE-A1""ピストンリング”4607
”2024-01-05""LINE-A2""クランクシャフト”2154
”2024-01-05""LINE-B2""ブレーキキャリパー”1806
”2024-01-08""LINE-C1""オルタネータ”1208
”2024-01-08""LINE-B1""ブレーキパッド”3904
”2024-01-09""LINE-A1""ピストンリング”4702
”2024-01-09""LINE-C1""スタータモータ”1003

結果の読み取り

  • SELECT * の10列から 5列 に絞られており、表が格段に読みやすくなっています
  • 報告書に不要な categoryshiftinspector_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)

検査日ライン部品名生産数不良数不良率
strstrstri64i64f64
”2024-01-04""LINE-A1""ピストンリング”45030.67
”2024-01-04""LINE-A2""クランクシャフト”22052.27
”2024-01-04""LINE-B1""ブレーキパッド”38020.53
”2024-01-05""LINE-A1""ピストンリング”46071.52
”2024-01-05""LINE-A2""クランクシャフト”21541.86
”2024-01-05""LINE-B2""ブレーキキャリパー”18063.33
”2024-01-08""LINE-C1""オルタネータ”12086.67
”2024-01-08""LINE-B1""ブレーキパッド”39041.03

結果の読み取り

  • 列ヘッダーが日本語に変わり、担当者向けの見やすい表 になりました
  • ROUND(defect_qty * 100.0 / production_qty, 2) AS 不良率
    計算式(不良率 = 不良数 ÷ 生産数 × 100)に 不良率 という分かりやすい名前を付けています
    ROUND(..., 2) は小数点2桁に丸める関数です
  • defect_qty * 100.0 / production_qty100.0(浮動小数点)が重要です
    100(整数)にすると整数除算になる可能性があるため、
    小数点の計算には 1.0100.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, PostgreSQLLIMIT n
SQL ServerSELECT TOP n ...
Oracle, 標準 SQLFETCH 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 で詳しく学びます)
    ここでは「全件数を確認するための関数」として使っています
  • LIMITORDER 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 BYWHEREGROUP 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)

検査日ライン部品名不良数
strstrstri64
”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 行取得



svg

保存完了: no008_order_by_desc.svg

結果の読み取り

  • DESC(降順) で不良数が多い順に並び、ワースト記録が先頭に来ます
    最大不良数は LINE-A1 / ピストンリング / 夜間 の15件 — 夜間シフトに着目すべきポイントです
  • 複合 ORDER BYline_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   テーブル名;
  • DISTINCTSELECT の直後に配置します
  • 複数列の 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_code7種類の部品コード が確認できます
    (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 エンジンの処理順):

実行順処理内容
1FROMテーブルを読み込む
2WHERE行の絞り込み
3GROUP BYグループ集計
4HAVING集計結果の絞り込み
5SELECT列の計算・別名の定義
6ORDER BY並び替え
7LIMIT件数制限

WHERESELECT(第5番)よりも先に実行(第2番)されるため、
SELECT で定義した別名は WHERE では使えません。
一方、ORDER BYSELECT の後なので、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)

部品名不良率生産数
strf64i64
”ショックアブソーバ”12.588
”ショックアブソーバ”10.090
”ショックアブソーバ”8.23529485
”スタータモータ”7.14285798
”オルタネータ”6.666667120
”スタータモータ”3.0100
”ブレーキキャリパー”2.857143175
”クランクシャフト”2.608696230
”ブレーキキャリパー”2.352941170
”クランクシャフト”2.272727220

# 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.


svg

保存完了: 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)の記録だけが絞り込まれ、降順で表示されます

  • 図解: 書く順序と実行順序を並べると、SELECTFROM の位置が入れ替わり、
    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 から sqlite3sqlalchemy で自動実行する

4. 品質管理部門への展開

  • 本シリーズ(No.001〜100)を社内 SQL 研修テキストとして活用する
  • 月次品質報告書用のクエリを整備して担当者全員が使えるようにする
  • SQL の結果を matplotlib / Polars で可視化するパイプラインを構築する

まとめ

本章(No.001〜010)では、SQL の基本構文を自動車部品メーカーの検査記録DBを題材に学びました。

No.学んだこと実務での使いどころ
001SELECT文の基本DBから最初のデータ取得の第一歩
002FROM句でテーブル指定検査記録 / 部品マスタを使い分ける
003SELECT *で全列取得新規テーブルの構造を素早く把握
004必要な列だけ取得報告書用データを最小限に絞り込む
005列に別名を付ける英語列名を日本語で出力・計算列に名前
006LIMITで件数制限大量データをN件だけサンプル確認
007ORDER BYで並べ替え不良数の少ない順・多い順でランキング
008昇順・降順の指定ワーストN件・ベストN件を瞬時に特定
009DISTINCTで重複除外部品コード・ライン種類を一覧で把握
010SQL実行順序の理解WHERE/ORDER BYのエラーの原因把握

次の章(第2章:条件指定と絞り込み No.011〜020) では、
WHERE 句による行のフィルタリングを学びます。
「不良数が10件以上のラインだけ抽出する」「特定ラインの記録だけ見る」
といった実務の核心操作を習得します。

法人向けのご相談

製造業の DX 推進・データ分析基盤の構築・社内 SQL / Python 研修については、
数理工房にお気軽にご相談ください。

📩 お問い合わせ: surikobo.co.jp/contact
まずはお気軽にご相談ください。


本記事は「データ分析のための SQL 入門 100本ノック」シリーズの第1章です。
シリーズ全体の目次は こちら からご確認ください。