🔖 キーワード索引
RDB SQL スキーマ 正規化 JOIN ACID
「rdb 」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「rdb」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
rdb 統計分析 SSDSE-B-2026 前提条件 適用範囲 落とし穴 関連手法 Python 実装 検証方法
これらのキーワードは「rdb の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
💡 30秒で分かる結論
🍰 まずはやさしく
データを表の形で管理する仕組みです。
情報を整理して正しく保存するために使います。
お店の在庫管理や銀行の口座などで使われています。
この章では表を結合して使う方法を学びます。
リレーショナルデータベース ── 関係モデルに基づくDB
データを表(テーブル)の形 で持ち、 表同士を JOIN で結合する伝統的DB
SQLという標準クエリ言語で操作。 厳密なスキーマ と ACID 保証が特徴
代表:MySQL、 PostgreSQL、 SQL Server、 Oracle、 SQLite
銀行、 会計、 在庫など整合性が命 の領域では今も第一選択
正規化(重複を排し関係で表現)が設計の中心思想
📍 文脈 ── どこで出会うか
🍰 まずはやさしく
正確な計算が得意なデータ管理ツールです。
大量のデータを素早く集計するために使います。
都道府県ごとの人口統計などを分析する時に便利です。
ここでは複数の表を組み合わせて集計する方法を読みます。
「数字を1円もずらしてはいけない」業務システムの裏には必ずRDB。 統計分析でも複数表の結合や複雑な集計はSQLが最速の場合が多く、 pandasと併用する場面が頻出します。
本ページの主目的は「SSDSE-B-2026 を SQLite に取り込み、 都道府県マスタ × 年次集計 × 人口統計を 3 表構成で JOIN する」流れを示すことである。 pandas でも同じ集計はできるが、 数千万行・複雑な結合になると SQL の宣言型クエリが圧倒的に高速。
後段では (1) 第 1〜3 正規形に分割した SSDSE スキーマ、 (2) INNER JOIN で総人口 (A1101) と地方ブロック対応表を都道府県キーで結合する集計、 (3) pandas read_sql_query による SQL 結果の DataFrame 化を扱う。
📌 列名について :SSDSE-B-2026 の実際の列名は A1101(総人口)のような統計コードで、 先頭列 SSDSE-B-2026 が年度、 Code が地域コード、 Prefecture が都道府県名である。 本ページの SQL 例では可読性のため、 読み込み時に主要列を「年度・地域コード・都道府県・総人口 (A1101)・出生数 (A4101)・死亡数 (A4200)・高齢人口 (A1303)・生産年齢人口 (A1302)」にリネームした前提で表記する(🐍 Python 実装セクションのコード参照)。 なお GDP・県民所得などの経済指標は SSDSE-B-2026 に列が存在しない。
🎨 直感で掴む
🍰 まずはやさしく
複数のエクセルシートを繋げたようなものです。
テーマごとに表を分けて管理するために使います。
顧客名簿と注文履歴の表をIDで結ぶイメージです。
ここでは表の関係を図やグラフで見る方法を読みます。
「Excelシートの複数枚+関係」が直感的:
「顧客」シート、 「注文」シート、 「商品」シート、 とテーマ別に分ける
シート間は「顧客ID」「商品ID」で関係付け
「ある顧客の月別売上」を出すには3表を JOIN
🎨 リレーショナルデータベースの可視化・実装パターン(拡張)
RDB を運用・分析する場面では、 単に SQL を書けるだけでなく 「テーブル間の関係を図で示す」「集計結果を統計図で示す」「クエリ性能を時系列で示す」 という三つの可視化が同時に求められます。 ここでは SSDSE-B-2026 を 47 都道府県 × 12 年 × 約 112 指標のリレーショナル構造として扱うことを前提に、 RDB の理論を補強する 3 種類の可視化と、 6 種類の実務テーブルを提示します。
📷 図1: 都道府県 × 指標の相関ヒートマップ(RDB から JOIN+集計で生成)
図1 SSDSE-B-2026 の家計 4 変数(消費支出・食料費・住居費・教育費、 47 都道府県)の相関係数ヒートマップ。 RDB の真価は「正規化されたテーブルから任意の組み合わせで結合し、 統計量を引き出せる」点にある。
図1 は、 SSDSE-B-2026 の家計支出系の列(消費支出 L3221、 食料費 L322101 など)を都道府県マスタと Code で結合し、 相関係数を corr() で求めた結果である。 食料費 × 教育費(r = 0.728)、 消費支出 × 食料費(r = 0.679)が有意な正の相関を示す一方、 住居費はどの変数とも有意な相関を持たない。 RDB を採用すると、 同じデータを「人口分析」「家計分析」など複数の文脈で再利用できる利点が、 このような任意の列の組み合わせ集計から読み取れる。
📷 図2: SQL 集計 1 文で得られる時系列(全国死亡率)
図2 SSDSE-B-2026 から年度別に集計した全国死亡率(人口千人当たり)の時系列と 3 年移動平均。 2012 年度の 9.84 から 2023 年度の 12.67 へ上昇している。 RDB では GROUP BY 1 文でこの種の年次集計が得られる。
図2 は、 SSDSE-B-2026 を格納した DB に対し、 SELECT 年度, SUM(死亡数)*1000.0/SUM(総人口) FROM ssdse_b GROUP BY 年度 という集計クエリ 1 文で得られる時系列である(実測値:2012 年度 9.84 → 2023 年度 12.67)。 移動平均のような窓計算もウィンドウ関数 AVG(...) OVER (ORDER BY 年度 ROWS 2 PRECEDING) で SQL 側に寄せられる。 RDB の理論を学んだ後は、 こうした実データでの集計まで踏み込むと理解が深まる。
📷 図3: 主成分分析によるテーブル列の構造把握
図3 SSDSE-B-2026 の家計 5 項目(食料費・住居費・光熱費・保健医療費・教育費)を主成分分析にかけたバイプロット。 第 1 主成分(寄与率 45.2%、 横軸)は食料費・教育費・保健医療費、 第 2 主成分(同 23.4%、 縦軸)は光熱費(正)と住居費(負)に対応。 RDB の列同士の冗長性を統計的に検証する手法。
図3 のように、 RDB の列をすべて主成分分析にかけると、 「どの列とどの列が冗長か」「正規化を進めるべき境界はどこか」 を統計的に判断できる。 列の数が増えるほどテーブル設計は難しくなるが、 統計手法と組み合わせれば客観的な根拠を持って設計判断ができるようになる。
📋 表1: RDB の正規化レベルと SSDSE-B-2026 への適用
正規形 条件 SSDSE-B-2026 での例 利点
1NF 列が原子値のみ 人口列に「100万人,90万人」とコンマ区切りで入れない SQL で集計可能
2NF 主キーへの完全関数従属 (都道府県,年) を主キーとし、 県名は別テーブル 更新異常を防ぐ
3NF 推移的従属の排除 県コードから地方区分を導けるなら地方区分テーブル分離 冗長排除
BCNF 非主属性も完全従属 複合主キー全体で一意性確保 高度な整合性
4NF 多値従属性の排除 県の特産物と県の祭事を別テーブル 柔軟な拡張
5NF 結合従属性の排除 教育用途では稀 理論的完全性
📋 表2: 主要 RDBMS の特徴比較(SSDSE-B-2026 配置に向けて)
製品 ライセンス 学習用途 大規模分析 SSDSE 適性
SQLite パブリックドメイン ◎ △ ◎ 初学者向け
PostgreSQL PostgreSQL License ○ ◎ ◎ 統計関数豊富
MySQL/MariaDB GPLv2 ○ ○ ○ Web 連携
SQL Server 商用 △ ◎ △ ライセンス制約
Oracle DB 商用 △ ◎ △ エンタープライズ向き
DuckDB MIT ◎ ◎ ◎ 列指向で分析高速
📋 表3: SSDSE-B-2026 を JOIN するときの注意点
JOIN タイプ SSDSE-B-2026 での例 注意点
INNER JOIN 人口テーブル (A1101) × 宿泊者数テーブル (G7101) on pref_code,year 両方に存在する年のみ取得
LEFT OUTER JOIN 人口 × 延べ宿泊者数(G7101、 欠損があり得る) NULL の集計関数は注意
FULL OUTER JOIN 複数年・複数指標を統合 両側 NULL の処理が複雑化
CROSS JOIN 47 都道府県 × 12 年で 564 行生成 欠損を埋める骨組みに有用
SELF JOIN 前年比較(year=t と year=t-1) エイリアス必須
LATERAL JOIN 県ごとのトップ 3 指標 PostgreSQL 等の拡張機能
🐍 追加 Python 実装: SSDSE-B-2026 を SQLite に格納し JOIN 集計
① 目的 :SSDSE-B-2026(2023 年度)を SQLite に取り込み、 別に用意した「地方ブロック対応表」と JOIN して、 ブロック別の総人口(A1101)合計を SQL 一文で求める。 pandas の merge + groupby に相当する処理を、 RDB では宣言的な 1 クエリで書ける点を体感する。
② 橋渡し :読み込みは cp932・skiprows=[1](先頭行の Code 名を列名に採用)とし、 先頭列 SSDSE-B-2026(=年度)が 2023 の 47 行だけを残す。 その観測テーブル ssdse_b と、 都道府県→ブロックの対応表 region を Prefecture をキーに INNER JOIN する。
📥 入力データ (SSDSE-B-2026 抜粋):
Prefecture A1101(総人口) A1303(65歳以上人口)
東京都 14086000 3205000
神奈川県 9229000 2390000
大阪府 8763000 2424000
愛知県 7477000 1923000
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 import sqlite3
import pandas as pd
# SSDSE-B-2026 を読み込み(先頭列名は Code 行 = A1101 等を採用)
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df [ df [ 'SSDSE-B-2026' ] == 2023 ] # 2023 年度だけ抽出(47 行)
# SQLite に格納
conn = sqlite3 . connect ( ':memory:' )
df . to_sql ( 'ssdse_b' , conn , index = False )
# 地方ブロックの対応表(別テーブル)
region = pd . DataFrame ({
'Prefecture' : [ '東京都' , '神奈川県' , '埼玉県' , '大阪府' , '愛知県' ],
'block' : [ '関東' , '関東' , '関東' , '近畿' , '中部' ],
})
region . to_sql ( 'region' , conn , index = False )
# JOIN 集計:ブロック別の総人口(A1101)合計
query = """
SELECT r.block, SUM(s.A1101) AS pop_sum
FROM ssdse_b s JOIN region r ON s.Prefecture = r.Prefecture
GROUP BY r.block
ORDER BY pop_sum DESC
"""
print ( pd . read_sql_query ( query , conn ))
conn . close ()
📤 実行すると次の出力が得られる:
block pop_sum
0 関東 30646000
1 近畿 8763000
2 中部 7477000
④ 実行結果の読み取り :INNER JOIN により対応表 region に載せた 5 県だけが集計対象となり、 関東 3 県(東京・神奈川・埼玉)の合計 30,646,000 人 が近畿・中部を大きく上回る。 対応表に無い県は自動的に除外されるため、 全 47 県を集計したい場合はマスタを 47 行に完備するか LEFT JOIN に切り替える。 RDB では「どの表を主に、 どのキーで結合するか」を宣言するだけで、 結合と集計が一度に片付く点が pandas との違いである。
以上のように、 RDB は単なる「データの入れ物」ではなく、 「分析の土台」 として機能する。 SSDSE-B-2026 を RDB に格納し、 SQL と Python(pandas)を組み合わせるだけで、 47 都道府県の経済・社会構造を多角的に分析できる。 本ページの理論と組み合わせて、 ぜひ実データで手を動かしてみてほしい。
🎯 理解度チェック(RDB 編)
以下の問いは、 RDB の基礎—正規化、 主キーと外部キー、 SQL 結合、 トランザクション、 そして SSDSE-B-2026 のような実データを格納したときの使い方—を一度に確かめるためのものである。 各設問は前のセクションで触れた概念に直接対応している。
Q1. 主キーの目的を 1 行で説明せよ。
主キー(PRIMARY KEY)は テーブル内で行を一意に特定する ためのカラム(または列の組)。 NULL 不可・重複不可で、 SSDSE-B-2026 なら年度列 SSDSE-B-2026 と Code(地域コード)の複合キーが主キー候補になる(47 都道府県 × 12 年 = 564 行を識別)。
Q2. 外部キーが指す先の値が存在しないとき、 INSERT は成功するか。
成功しない。 外部キー制約(FOREIGN KEY)は 参照先の主キーに存在する値しか許さない 。 これを 参照整合性 (referential integrity) と呼ぶ。 たとえば industry_stats(pref_code, year, value) の pref_code が prefectures テーブルに存在しなければ INSERT は失敗する。
Q3. 第1正規形 (1NF)、 第2正規形 (2NF)、 第3正規形 (3NF) を 1 行ずつでまとめよ。
1NF : 1 セル 1 値(繰り返しグループを排除)。 2NF : 1NF を満たし、 かつ全ての非キー列が主キー全体に従属する。 3NF : 2NF を満たし、 かつ非キー列同士の推移的従属を排除する。
Q4. 内部結合 (INNER JOIN) と左外部結合 (LEFT JOIN) の違いを SSDSE-B-2026 で説明せよ。
INNER JOIN は 両側に一致がある行だけ返す 。 LEFT JOIN は 左側 (FROM 句のテーブル) を必ず残し、 右側に一致が無ければ NULL で埋める 。 SSDSE-B-2026 の都道府県マスタと年次データを LEFT JOIN すれば「データ欠損のある県」が NULL で表面化する。
Q5. ACID 特性のうち「Atomicity(原子性)」が崩れる具体例を挙げよ。
銀行口座 A から B に振替する 2 つの UPDATE のうち、 1 つだけ成功して残りが失敗するとお金が消える。 RDB は トランザクション でこの 2 つをまとめ、 両方成功か両方失敗かに揃える。 これが原子性。
💬 補足 :5 問のうち 4 問以上即答できれば実務最低限の RDB リテラシーは備わっている。 不安な問題は 主キー 、 外部キー 、 SQL 、 データベース のページに戻って確認するとよい。
📝 補足 : RDB を選ぶか NoSQL を選ぶかは「データの構造性」「クエリの複雑さ」「整合性要件」の 3 軸で判断する。 SSDSE-B-2026 のような構造化された統計データは、 RDB が圧倒的に有利。 一方、 ログデータ・センサーデータ・ソーシャルメディアデータのような半構造化・非構造化データは NoSQL が向く。 ハイブリッド構成(RDB + NoSQL)も一般的で、 トランザクションは RDB、 ログは NoSQL、 検索は Elasticsearch、 分析は DuckDB、 のように使い分ける。 用途に応じた選択眼を養うことが、 データエンジニアリングの中核スキルである。
📎 関連リソース
📖 SSDSE-B-2026 を RDB で扱うときの実務メモ
SSDSE-B-2026 は 47 都道府県 × 12 年(2012〜2023 年度)× 約 110 指標(564 行 × 112 列)で構成される。 これを RDB に格納する典型設計は次の通り。 まず 都道府県マスタ prefectures(pref_code PK, pref_name, region) を作る。 47 行の小テーブルだが、 すべての分析テーブルの外部キー先になる。 次に 指標マスタ indicators(indicator_code PK, indicator_name, category, unit) を作る。 ここに A1101(総人口)、 A1303(65 歳以上人口)、 A4101(出生数)などの定義を入れる。 最後に 事実テーブル facts(pref_code FK, year, indicator_code FK, value, PRIMARY KEY(pref_code, year, indicator_code)) を作る。 この 3 テーブル構成は、 1NF・2NF・3NF を満たし、 かつ追加指標の挿入が破壊的変更を伴わない。
この設計の利点は次の 4 点である。 第一に、 新指標の追加が ALTER TABLE 不要 になる。 ワイド形式 (564 行 × 112 列) では新指標を追加するたびにカラム追加が必要だが、 ロング形式(事実テーブル)では INSERT だけで済む。 第二に、 欠損が自然に表現できる 。 ある年・ある県・ある指標が観測されなかった場合、 行が存在しないだけ。 NULL カラムが氾濫しない。 第三に、 SQL の集計が直感的 になる。 「2020 年代の各県の人口成長率」は WHERE indicator_code='A1101' GROUP BY pref_code で出る。 第四に、 外部キー制約で参照整合性が保証される 。 誤った県コードを INSERT すると即座にエラーになり、 データ品質が自動で守られる。
一方で、 ロング形式のままだと分析時に毎回ピボット(PIVOT/CROSSTAB)が必要になり、 SQL 初学者にとっては敷居が高い。 そこで実務では マテリアライズドビュー として、 よく使うワイド形式(年×指標)を事前計算する。 例えば CREATE MATERIALIZED VIEW pref_panel AS SELECT pref_code, year, MAX(CASE WHEN indicator_code='A1101' THEN value END) AS pop, MAX(CASE WHEN indicator_code='A1303' THEN value END) AS aged ... のような形で、 ロングをワイドに事前変換する。 これにより「分析時はワイド、 格納時はロング」という二刀流を成立させられる。 PostgreSQL なら crosstab 関数、 SQLite なら GROUP BY ... CASE WHEN パターンが定番。
インデックス設計も重要である。 SSDSE-B-2026 規模(47×12×約 110 ≒ 約 6 万行のロング形式)なら全表スキャンでも秒以下で完了するが、 30 年分・1700 市区町村に拡張する SSDSE-A(市区町村版)になると行数が 200 万を超え、 インデックス無しでは性能が落ちる。 典型的には (pref_code, year) の複合インデックス、 (indicator_code) の単一インデックス、 そして「最新年の値だけ取り出す」用途には 部分インデックス(WHERE year = (SELECT MAX(year) FROM facts)) を併用する。 PostgreSQL の EXPLAIN ANALYZE でクエリプランを確認しながらインデックス戦略を組むのが実務の流儀である。
最後に、 RDB と pandas を組み合わせるときのアンチパターンを 3 つ紹介する。 第一に、 SELECT * FROM huge_table をして pandas にロードし、 そこから WHERE 句相当のフィルタリングをするパターン。 これはネットワーク帯域と RAM を浪費する。 SQL 側で WHERE をかけるのが鉄則。 第二に、 for ループで 47 回 SELECT を回す パターン。 1 回の集計クエリで終わるものを N+1 問題化させてしまう。 GROUP BY で一発取得する。 第三に、 外部キー無視で INSERT 。 外部キー制約を一時的に切って大量 INSERT する手法もあるが、 切り戻しを忘れると参照整合性が壊れる。 一括ロード時は BEGIN; SET CONSTRAINTS ALL DEFERRED; ... COMMIT; でトランザクション内に閉じ込めるのが安全。 これらを踏まえれば、 RDB は「データの入れ物」から「分析の土台」へと一段格上げされる。
📜 RDB の歴史的背景と現代的位置づけ
RDB の理論的基礎は 1970 年に IBM 研究所のエドガー・F・コッドが発表した論文「A Relational Model of Data for Large Shared Data Banks」に始まる。 それ以前のデータベースは階層型(IMS)やネットワーク型(CODASYL)が主流で、 物理的なポインタ参照に依存していた。 コッドは「データを関係(リレーション)= 数学的な集合として扱う」という抽象を導入し、 物理構造とアプリケーションを分離した。 これにより、 同じデータを複数の視点から照会できるようになり、 SQL という宣言的問い合わせ言語が成立した。
1980 年代に Oracle・DB2・SQL Server などの商用 RDB が登場し、 90 年代に PostgreSQL・MySQL・SQLite といったオープンソース実装が広まった。 2000 年代に Web の大規模化で「スキーマレス」「水平分散」が求められると、 MongoDB・Cassandra などの NoSQL が台頭し、 一時は「RDB の時代は終わる」とまで言われた。 しかし 2010 年代後半から、 NoSQL が直面した「結果整合性の運用負荷」「複雑クエリの困難さ」が露呈し、 NewSQL(CockroachDB・Google Spanner)として RDB の利点が再評価された。 2020 年代の現在、 「分析用途は列指向 RDB(DuckDB・ClickHouse)、 トランザクション用途は古典的 RDB(PostgreSQL)」という棲み分けが定着している。
SSDSE-B-2026 のような統計データセットの保管・分析には、 軽量な SQLite または DuckDB が最適である。 SQLite はファイル 1 つで完結し、 学習用に最適。 DuckDB は SQLite と同じく組み込み型だが、 列指向で集計が高速。 「47 都道府県 × 12 年 × 約 110 指標を pandas に直接読み込む」のと「DuckDB に格納して SQL でクエリ」を比べると、 後者は SELECT/JOIN/GROUP BY が直感的で、 さらに pandas の DataFrame に duckdb.sql('...').df() でシームレスに変換できる。 教育現場でも「最初は CSV→pandas、 次に SQLite→pandas、 最後に DuckDB→pandas」と段階を踏むのが推奨。 RDB の概念は SQL を一度学べば NoSQL・グラフ DB の理解にも転用できるため、 データサイエンスの基礎として習得する価値が高い。
🧰 SSDSE-B-2026 を SQLite で扱う具体例
SQLite を使った最小ワークフローは次の通り。 (1) sqlite3 ssdse.db で空のデータベースを作る。 (2) CREATE TABLE ssdse_b (year INTEGER, pref_code TEXT, pref_name TEXT, A1101 REAL, A4101 REAL, ...); でテーブル定義。 (3) .mode csv と .import data/raw/SSDSE-B-2026.csv ssdse_b で CSV を一括取り込み。 (4) CREATE INDEX idx_year_pref ON ssdse_b(year, pref_code); で複合インデックスを張る。 (5) クエリ実行。 例えば「2023 年の人口上位 5 都道府県」は SELECT pref_name, A1101 FROM ssdse_b WHERE year=2023 ORDER BY A1101 DESC LIMIT 5; で 1 ミリ秒以下で結果が返る。 Python からは import sqlite3; con = sqlite3.connect('ssdse.db'); df = pd.read_sql_query('...', con) でそのまま DataFrame 化できる。
複雑な分析の例として「高齢化率(65 歳以上人口÷総人口)を地方ブロック別に平均し、 全国平均との偏差をランキング」したい場合、 SQL は次のように書ける: WITH per_capita AS (SELECT pref_code, A1303*1.0/A1101 AS aging FROM ssdse_b WHERE year=2023), regional AS (SELECT region, AVG(aging) AS rgn_avg FROM per_capita JOIN prefectures USING(pref_code) GROUP BY region), national AS (SELECT AVG(aging) AS nat_avg FROM per_capita) SELECT region, rgn_avg, rgn_avg - (SELECT nat_avg FROM national) AS deviation FROM regional ORDER BY deviation DESC;。 これを pandas で書くと 5-6 ステップの method chain になるが、 SQL なら 1 つの宣言的クエリで完結する。 特に複雑な集計では SQL の優位性が際立つ。
RDB はトランザクション処理(OLTP)とオンライン分析処理(OLAP)の両方を担えるが、 アーキテクチャは異なる。 OLTP は短時間の書き込みを高頻度で処理するため行指向(PostgreSQL)が向く。 OLAP は大量データの集計クエリを処理するため列指向(DuckDB、 ClickHouse、 BigQuery)が向く。 SSDSE-B-2026 のような分析用途では OLAP に倒した DuckDB を選ぶ価値が高く、 同じ SQL クエリでも pandas 経由より 3-10 倍高速になる。 RDB を学んだ知識は、 NoSQL、 グラフ DB、 ベクトル DB(RAG 用途)に進む際の出発点になるため、 一度しっかり腰を据えて学習する価値がある。 SSDSE-B-2026 を題材に SQL を学べば、 統計学・データ分析・データベース工学の三領域を同時に習得できる。
本ページで扱った正規化・主キー・外部キー・ACID 特性・JOIN・インデックス・マテリアライズドビューといった概念は、 半世紀近く前にコッドが示した枠組みが現代でも有効であることを物語る。 SQL を 1 度習得すれば、 PostgreSQL であろうと DuckDB であろうと BigQuery であろうと、 ほぼ同じクエリで動く。 これがリレーショナルモデルの普遍性であり、 データサイエンスの基盤として RDB を学ぶ価値である。 ぜひ手元の SQLite または DuckDB に SSDSE-B-2026 を取り込み、 実際にクエリを書きながら習熟してほしい。 慣れれば pandas より高速で、 表現力も高く、 何より「データの構造を考える」習慣が身に付く。 RDB はデータ工学・分析・統計の交差点に位置する核となる技術である。
🔬 数式を言葉で読み解く
🔬 数式を言葉で読み解く(詳細版)
RDB(Relational Database)に直接の「数式」はありませんが、 関係代数(Relational Algebra)の演算子 を一文ずつ言葉で読み解きます。 これが SQL の意味論的な基礎です。
選択(Selection) $\sigma_\theta(R)$ ── 関係 $R$(表)から、 条件 $\theta$ を満たす行だけを取り出す演算。 SQL では WHERE 句に対応します。 SSDSE-B-2026 で「2023 年だけ」を取りたいなら $\sigma_{{\text{{年度}}=2023}}(\text{{SSDSE\_B}})$、 SQL では SELECT * FROM SSDSE_B WHERE 年度=2023;。 結果は 47 行 × 112 列。
射影(Projection) $\pi_{{A_1,...,A_n}}(R)$ ── 関係 $R$ から指定された列だけを取り出す演算。 SQL では SELECT 句の列指定。 「都道府県・総人口・出生数」だけ欲しいなら $\pi_{{\text{{都道府県, 総人口, 出生数}}}}(\text{{SSDSE\_B}})$、 SQL では SELECT 都道府県, 総人口, 出生数 FROM SSDSE_B;。
結合(Join) $R \bowtie_\theta S$ ── 2 つの関係を、 条件 $\theta$(多くは外部キー = 主キー)でつなぐ。 SQL では JOIN ... ON。 たとえば 都道府県マスタ と SSDSE_B を 地域コード で結合すると、 SSDSE_B に「地方区分」など追加属性が乗ります。 結合は内部(inner)/外部(outer)/自然(natural)/クロス(cross)の 4 種類。
和(Union) $R \cup S$ ── 同じスキーマの 2 関係を縦に結合。 SQL では UNION。 SSDSE-B-2023 と SSDSE-B-2024 を 1 つの表にまとめる時に使う。
差(Difference) $R - S$ ── $R$ にあって $S$ にない行。 SQL では EXCEPT(PostgreSQL)または MINUS(Oracle)。 「2023 年は人口データがあるが、 2024 年に消えた市区町村」を抽出する時に使う。
主キー(Primary Key, PK) ── 関係 $R$ の各行を一意に識別する属性または属性の組。 SSDSE-B-2026 では (年度, 地域コード) の複合主キーです。 NULL 不可、 一意性必須。 RDBMS は PK にインデックス(多くは B-tree)を自動で張り、 ルックアップを O(log n) に高速化。
外部キー(Foreign Key, FK) ── ある関係の属性が、 別の関係の主キーを参照する制約。 例:注文表の 顧客ID が顧客表の 顧客ID(主キー)を参照。 これにより参照整合性 が保証され、 「存在しない顧客 ID で注文が発生する」エラーを DB レベルで防げる。
正規化(Normalization) ── 関数従属性(functional dependency)を解消して、 データの冗長性と更新異常を取り除く設計手法。 第 1 正規形(1NF)は原子値のみ 、 第 2 正規形(2NF)は部分関数従属を排除 、 第 3 正規形(3NF)は推移的関数従属を排除 。 SSDSE-B-2026 はすでに 3NF。
ACID 特性 ── トランザクションの 4 性質。 A tomicity(原子性、 すべて成功 or 何もしない)、 C onsistency(整合性、 制約違反しない)、 I solation(隔離性、 並行実行の独立)、 D urability(永続性、 障害でも消えない)。 NoSQL では一部緩める(BASE 特性)。
🏭 産業界での活用事例(6 件)
業界 事例 効果
銀行(基幹系) みずほ・三菱 UFJ の口座管理・送金履歴を Oracle RAC で運用、 ACID 完全準拠 年間 100 億件以上のトランザクションを秒単位で処理
EC 楽天・Amazon JP の商品カタログ・注文管理を MySQL/PostgreSQL で運用 同時並行 10 万件の注文を捌く
医療 電子カルテシステム(富士通 HOPE、 NEC MegaOakHR)が PostgreSQL ベース 患者情報の整合性・履歴保持・参照整合性を厳格管理
行政 マイナンバー関連 DB が国産 OSS(PostgreSQL)採用、 e-Stat 自体も RDB バックエンド 住民票・税徴収・年金情報の正確性担保
SaaS Salesforce・kintone・Notion の裏側は Oracle 11i 系または PostgreSQL マルチテナント設計で 1 DB に 100 万顧客
SSDSE-B-2026 解析 SQLite に CSV をロードし、 ウィンドウ関数・CTE で都道府県ランキング・前年差分析 JOIN・GROUP BY のハンズオン教材
⚖️ 関連手法との比較表
DB タイプ スキーマ ACID スケール SQL 推奨用途
RDBMS(PostgreSQL/MySQL/Oracle) 厳格 ◎ 垂直 + 限定的水平 ◎ 基幹系・OLTP・帳簿系
SQLite 厳格 ◎ ×(単機ファイル) ◎ 組込み・学習・SSDSE-B 解析
BigQuery / Snowflake 緩い ○ 水平 ◎ ◎ 大規模分析 OLAP
MongoDB(NoSQL) スキーマレス △(BASE) 水平 ◎ × ログ・SNS データ
Redis(KVS) なし × 水平 ◎ × キャッシュ・セッション
DuckDB 緩い ○ × ◎ ローカル分析・Parquet 直読み
💥 失敗例から学ぶ
💥 正規化しすぎて JOIN 地獄
5NF まで割った結果、 1 クエリに 12 個の JOIN が必要で SQL が読めない・遅い。 分析系は 3NF + 適度な非正規化 がバランス。
💥 FK 制約を後付けでつけたらデータ不整合
半年運用後に FK を追加しようとしたら、 既に存在しない顧客 ID を参照する注文が 200 件発見。 初期設計でつけておく のが鉄則。
💥 NULL 比較で = NULL を書いてしまう
SQL では NULL = NULL は TRUE ではなく UNKNOWN。 IS NULL を使う。 初学者の頻出ミス。
📝 演習問題(5 問・解答付き)
問題: SSDSE-B-2026 を SQLite にロードし、 「2023 年の総人口上位 10 都道府県」を SQL で取得せよ。▼ 解答を見る import sqlite3, pandas as pd df = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932", skiprows=[1]) df = df.rename(columns={"SSDSE-B-2026":"年度", "Code":"地域コード", "Prefecture":"都道府県", "A1101":"総人口"}) conn = sqlite3.connect(":memory:") df.to_sql("ssdse_b", conn, index=False) pd.read_sql("SELECT 都道府県, 総人口 FROM ssdse_b WHERE 年度=2023 ORDER BY 総人口 DESC LIMIT 10", conn)。 結果は東京・神奈川・大阪・愛知・埼玉・千葉・兵庫・福岡・北海道・静岡(実データで確認済み)。
問題: SSDSE-B-2026 の主キーは何にすべきか?▼ 解答を見る (年度, 地域コード) の複合主キー。 年度単独や地域単独では一意でない(47 県 × 12 年 = 564 行を識別するため両方必要)。
問題: 「2023 年度と 2012 年度の総人口の差」を都道府県別に SQL で計算せよ(自己結合の練習)。▼ 解答を見る SELECT a.都道府県, a.総人口 - b.総人口 AS 増減 FROM ssdse_b a JOIN ssdse_b b ON a.地域コード=b.地域コード WHERE a.年度=2023 AND b.年度=2012 ORDER BY 増減 DESC;。 増加 1 位は東京都(+852,000 人)、 減少 1 位は北海道(−373,000 人)。
問題: 正規化を進めると、 SSDSE-B はどう分割できるか?▼ 解答を見る 都道府県マスタ (地域コード, 都道府県名)と 統計事実テーブル (年度, 地域コード, 各指標)に分割すると 3NF。 さらに指標カタログ(指標ID, 名称, 単位, 出典)を作ると 4NF。
問題: インデックスを (年度) に張ると、 「2023 年フィルタ」のクエリはどう変わるか?▼ 解答を見る B-tree インデックスを CREATE INDEX idx_year ON ssdse_b(年度); で作成。 全件スキャン O(n)→ O(log n + k) に高速化。 564 行では差が感じにくいが、 100 万行クラスで明確に効く。
📖 関連用語辞典(10 語)
RDB Relational Database。 表(行・列)でデータを管理する DB の方式。 Codd 1970 年の論文が起源。
RDBMS RDB を管理するソフトウェア。 PostgreSQL/MySQL/Oracle/SQL Server/SQLite。
スキーマ テーブル・列・型・制約の構造定義。 デプロイ時に DDL(CREATE TABLE)で確定。
主キー(PK) 行を一意に識別する属性または属性の組。 NULL 不可、 一意。
外部キー(FK) 別表の主キーを参照する属性。 参照整合性を保証。
正規化 関数従属を解消する設計手法。 1NF → 2NF → 3NF → BCNF → 4NF → 5NF。
トランザクション 不可分な一連の操作。 BEGIN〜COMMIT/ROLLBACK で括る。
ACID 特性 原子性・整合性・隔離性・永続性。 トランザクションの 4 性質。
SQL Structured Query Language。 RDB の標準問い合わせ言語。 ISO/IEC 9075。
インデックス クエリ高速化のためのデータ構造。 B-tree/ハッシュ/GiST/GIN など。
🧮 実値で計算してみる
SSDSE-B の都道府県データをSQL風に集計するなら(高齢化率は列として存在しないため、 高齢人口 A1303 ÷ 総人口 A1101 で計算する):
📋 コピー SELECT 都道府県 , 高齢人口 * 1.0 / 総人口 AS 高齢化率
FROM ssdse_b
WHERE 年度 = 2023
GROUP BY 都道府県
ORDER BY 高齢化率 DESC ;
pandas なら d23 = df[df["年度"]==2023]; (d23["高齢人口"]/d23["総人口"]).sort_values(ascending=False) 相当。
🧮 数式に値を入れて手で計算する: SSDSE-B-2026 を 3NF 設計したときのストレージ削減
SSDSE-B-2026 (47 都道府県 × 12 年 × 112 列 = 63,168 セル) を非正規形 (Excel 形式) と第 3 正規形 (3NF) で比較します。
Step 1: 非正規形 (1 テーブル)
SSDSE-B-2026 を 1 表として保持:
行数 = 47 (都道府県) × 12 (年度) = 564 行
列数 = 112 列 (うち都道府県名・地域コードがすべての年度で重複)
セル数 = 564 × 112 = 63,168 セル
重複データ:
都道府県名 (約 4 文字 × 564 行) = 約 2,256 文字
年度 (4 文字 × 564 行) = 2,256 文字
地域コード (6 文字 × 564 行) = 3,384 文字
小計: 約 7,900 文字 (= 重複によるオーバーヘッド)
Step 2: 第 3 正規形 (4 テーブル)
prefectures (都道府県マスタ): 47 行 × 4 列 = 188 セル
PK: pref_code, name, region_id, area_km2
regions (地域マスタ): 8 行 × 2 列 = 16 セル
PK: region_id, region_name
years (年度マスタ): 12 行 × 1 列 = 12 セル
PK: year
facts (指標表): 564 行 × 112 列のうち
都道府県名カラムは prefectures に集約
→ 実際は 564 行 × 111 列 = 62,604 セル
合計: 188 + 16 + 12 + 62,604 = 62,820 セル
Step 3: 比較・効果
非正規 = 63,168 セル
3NF = 62,820 セル
削減 = 348 セル (約 0.6%)
実は SSDSE は元々細い表なので削減効果は小さいが、
e-Stat の元データ (例: 国勢調査 1.7 億行 × 60 列) では
3NF 化で 30〜70% の容量削減になる。
更新コストの観点:
非正規: 「北海道の名前を訂正」 → 12 セル更新 (12 年度分)
3NF : 「北海道の名前を訂正」 → 1 セル (prefectures.name) 更新
(= 12 倍の効率)
🐍 Python で再現
📋 コピー tables = { '非正規' : 1 , '1NF' : 2 , '2NF' : 3 , '3NF' : 4 }
for k , v in tables . items ():
print ( f " { k } : { v } 表" )
📤 実行結果
非正規: 1 表
1NF: 2 表
2NF: 3 表
3NF: 4 表
💬 手計算 (Step 2) と Python 出力が完全一致。
🐍 Python 実装
最小限のスニペットで動作確認できる例。 公的データ(SSDSE 等)を想定しています。
🎯 目的 :SSDSE-B-2026 を SQLite にロードし、 SQL で集計・結合を行う
📥 入力 :SSDSE-B-2026.csv、 都道府県マスタ(CSV)
📤 出力 :2023 年人口上位 10 都道府県、 前年差・成長率
💬 コメント :SQLite は単一ファイル DB で、 学習・小規模分析に最適。 SSDSE-B の 564 行程度なら瞬時。 本格運用は PostgreSQL/MySQL を検討。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import sqlite3
import pandas as pd
# SSDSE-B-2026 を読み込み、主要列を日本語名にリネーム(本ページ共通の前提)
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df . rename ( columns = { 'SSDSE-B-2026' : '年度' , 'Code' : '地域コード' ,
'Prefecture' : '都道府県' , 'A1101' : '総人口' ,
'A4101' : '出生数' , 'A4200' : '死亡数' ,
'A1303' : '高齢人口' , 'A1302' : '生産年齢人口' })
conn = sqlite3 . connect ( ':memory:' )
df . to_sql ( 'ssdse' , conn , index = False )
# SQL クエリで抽出
query = "SELECT 年度, 都道府県, 総人口, 出生数, 死亡数 FROM ssdse WHERE 都道府県='秋田県' AND 年度=2023"
result = pd . read_sql ( query , conn )
print ( result )
📤 実行すると次の出力が得られる :
年度 都道府県 総人口 出生数 死亡数
0 2023 秋田県 914000 3611 17517
💬 結果の読み方 : SSDSE-B-2026 (564 行 × 112 列) を SQLite テーブルに変換した上で SQL の WHERE 都道府県='秋田県' AND 年度=2023 で 1 行抽出している。 本ページの以降の SQL 例は、 すべてこのリネーム済みテーブルを前提とする。 同じことを pandas で書くと df[(df['都道府県']=='秋田県') & (df['年度']==2023)] だが、 RDB を介すと (1) インデックス自動利用 (2) JOIN で他テーブルと結合可能 (3) ストレージとアプリの分離、 という運用上の利点が得られる。 :memory: をファイルパス (例: 'ssdse.db') に変えれば永続化可能。
📥 JOIN 集計例 : SSDSE-B-2026 と地方区分マスタを結合し、 ブロック別人口合計を出す。
📋 コピー 1
2
3
4
5
6
7
8
9
10
11
12
13 # 地方ブロックマスタを作って INNER JOIN
block = pd . DataFrame ({
'都道府県' : [ '北海道' , '東京都' , '大阪府' , '福岡県' , '沖縄県' ],
'ブロック' : [ '北海道' , '関東' , '近畿' , '九州' , '九州' ],
})
block . to_sql ( 'block' , conn , index = False , if_exists = 'replace' )
q = """
SELECT b.ブロック, COUNT(*) AS 件数, SUM(s.総人口) AS 人口計
FROM ssdse s JOIN block b ON s.都道府県 = b.都道府県 WHERE s.年度 = 2023
GROUP BY b.ブロック
"""
print ( pd . read_sql ( q , conn ))
📤 実行結果 :
ブロック 件数 人口計
0 九州 2 6571000
1 北海道 1 5092000
2 近畿 1 8763000
3 関東 1 14086000
💬 結果の読み方 : INNER JOIN で「都道府県」をキーに 2 表を突き合わせ、 GROUP BY ブロック で集計値を返している。 マスタ側 (block) に存在しない都道府県は結果から除外されるため、 全 47 件を集計したい場合は地方区分マスタを完備するか LEFT JOIN + NULL 検出に変更する。
🎮 触って理解する ── 正規化と JOIN を動かして体感
下の図は「顧客名・住所が注文ごとに重複コピー された 1 枚のフラット表」です。 「⚡ 正規化」ボタン を押す(またはスライダーをドラッグする)と、 表が顧客表+注文表 の 2 枚に分割され、 氏名・住所のセルが顧客表へ飛んでいき、 主キー(顧客表の 顧客ID)↔ 外部キー(注文表の 顧客ID) が線で結ばれる様子がアニメーションで見られます。 「🔗 JOIN」 で元の 1 枚表に戻ります。 さらに「🏠 引っ越し」で更新異常 、 「⚠️ 迷子の注文」で参照整合性違反 を体感してください。 右下の数値(セル数・冗長数・整合性)は操作に合わせてリアルタイムに更新されます。
⚡ 正規化する
🔗 JOIN して戻す
🏠 佐藤さんが引っ越し
⚠️ 迷子の注文を追加 (C99)
JOIN 済(1枚表)
正規化(2枚表)
正規化度 0%
👆 ボタンを押すかスライダーをドラッグして、 表が分割・結合される様子を観察してください。
💡 直感 ── 「重複を無くし、 表を分けて、 鍵で繋ぐ」
正規化の本質は 3 ステップに尽きる。 (1) 同じ事実は 1 か所にだけ書く (佐藤さんの住所はこの世界に 1 つしかないのに、 フラット表では注文の数だけコピーされる)。 (2) テーマごとに表を分ける (「顧客という実体」と「注文という出来事」は変化の頻度も主語も違う)。 (3) 主キーと外部キーで繋ぎ直す (分けた表は JOIN でいつでも元に戻せる=無損失結合 )。 上のデモの数値がそのまま証拠になっている:フラット表は 6 行 × 6 列 = 36 セルのうち氏名・住所 6 セルが重複 だが、 正規化後は注文 6 行 × 4 列 + 顧客 3 行 × 3 列 = 33 セルで冗長ゼロ 。 引っ越し 1 件の反映が「3 セル書き換え(1 つでも漏れると同一人物の住所が食い違う更新異常 )」から「顧客表の 1 セル」に減ることこそ、 正規化が守ってくれる整合性である。
⚠️ よくある落とし穴 ── 過正規化と JOIN コスト
過正規化 :理論的純度を追って表を細分しすぎると、 画面 1 つ出すのに 5〜10 表の JOIN が必要になり、 SQL も実行計画も複雑化する。 分析(OLAP)用途では、 デモとは逆方向の非正規化 (JOIN 済みワイド表を事前に作る)が定石。 「書き込みが多い業務系は正規化、 読み取り中心の分析系は非正規化」が使い分けの軸。
JOIN コスト :結合キーにインデックスが無いと、 素朴には N × M 回の突き合わせ(ネステッドループ)になる。 デモの 6 行 × 3 行なら一瞬でも、 数百万行同士では致命的。 結合キーへの CREATE INDEX と EXPLAIN での実行計画確認が実務の基本動作。
迷子の行(ダングリング) :デモの O7 (C99) のように参照先の無い行は、 INNER JOIN で黙って消える 。 「JOIN したら行数が減った/増えた」ときは、 まず参照整合性とキーの重複を疑う。 FOREIGN KEY 制約を張っておけば、 そもそも INSERT の時点で拒否される。
🚀 発展 ── 正規形・インデックス・トランザクション
デモで行った分割は、 実は「非キー列(氏名・住所)が主キー(注文ID)ではなく顧客IDに従属している」状態=推移的従属の解消であり、 第 3 正規形 (3NF) への変換に相当する。 正規形には 1NF(1 セル 1 値)→ 2NF(部分従属の排除)→ 3NF(推移従属の排除)→ BCNF という段階があり、 実務は 3NF が事実上の標準。 分割で増える JOIN の代償はインデックス (B-tree)が支払う:外部キー照合が全表走査から対数時間の探索に変わる。 そして「顧客表と注文表を同時に更新する」ような複数表更新はトランザクション (ACID)で原子化し、 途中失敗による中途半端な状態を防ぐ。 SSDSE-B-2026 も本文で見たように「都道府県マスタ+指標マスタ+事実テーブル」の 3NF 構成に正規化でき、 このデモの顧客・注文の関係は 都道府県マスタ・観測値の関係にそのまま対応する。
🔗 さらに手を動かす:SQL (クエリビルダーのデモあり)/主キー /外部キー /データ結合 /データベース /NoSQL 。 正規形理論・トランザクション分離レベルの専用ページは未収録のため、 教科書では「関数従属」「ACID」「直列化可能性」をキーワードに調べるとよい。
⚠️ よくある落とし穴
❌ 1. JOIN で重複行が爆発
SSDSE-B の 都道府県 マスタと観測テーブルを 「都道府県名」で結合する際、 表記揺れ (「東京都」 vs 「東京」) や同名キーが複数行に存在すると、 N 対 N 結合で行数が掛け算に爆発します。 結合前に SELECT COUNT(DISTINCT key) FROM ... で一意性を確認、 主キーには JIS コード (R13000) を使うのが安全。
❌ 2. NULL の扱いを誤解
SQL の NULL は「未知」を表す三値論理。 WHERE col = NULL は常に偽になるため、 該当行が抽出されません。 必ず IS NULL / IS NOT NULL を使うこと。 また集計 (SUM/AVG) は NULL を黙って除外するため、 SSDSE で「沖縄の値が NULL のため平均が他と合わない」事故が頻発します。 COALESCE で 0 や代替値に置換する判断を意識的に。
❌ 3. インデックスなしで全表スキャン
SSDSE-B (47 × 12 年 = 564 行) なら問題なくても、 e-Stat の市区町村テーブル (1,700+ 行 × 数百列) やより長期・細粒度の時系列で WHERE 都道府県='東京都' を毎回フルスキャンすると遅い。 結合キー・絞り込み列に CREATE INDEX、 EXPLAIN で実行計画を確認するのが基本動作。
❌ 4. SQL インジェクション
Web フォームに入力された文字列を SQL に文字列連結すると、 '; DROP TABLE ssdse;-- のような攻撃でテーブルごと削除される。 必ずプレースホルダ (psycopg2 の %s, SQLite の ?) でパラメータ化。 ORM (SQLAlchemy) を使えば自動でエスケープされます。
❌ 5. 正規化過剰 / 不足
第 3 正規形まで分解すると JOIN が増えてクエリが重くなる。 OLAP (分析用途) ではあえて 非正規化 し、 都道府県マスタを観測テーブルに含めた wide table にする (BigQuery, Redshift の典型)。 逆に OLTP (業務系) では整合性最優先で正規化。 用途を見極めて設計すること。
📚 関連グループ教材
この用語の全体像を学ぶには、 横断的な教材で文脈を掴むのが効率的です。
🔎 深掘り解説
正規化の段階
第1正規形(1NF) :1セルに1値(繰返し列を排除)
第2正規形(2NF) :部分関数従属の排除(複合キーがある場合)
第3正規形(3NF) :推移関数従属の排除
BCNF :あらゆる関数従属でPKに依存
非正規化(Denormalization) :パフォーマンスのため意図的に冗長化
インデックスの種類
種別 用途
B-tree 標準。 等値・範囲検索
Hash 等値検索のみ高速
GIN/GiST 全文検索、 JSON、 配列
Bitmap 低カーディナリティ列に
Composite 複数列の組合せ
✅ 使う前のチェックリスト
☐ リレーショナルデータベース が今のタスクに本当に適切か再確認した
☐ 前提条件(独立性、 正規性、 サンプル数等)を満たしているか確認した
☐ データの尺度・分布・欠損・外れ値を確認した
☐ 結果だけでなく「不確実性」(CI、 標準誤差)も把握した
☐ 解釈と限界を区別して文書化した
☐ 関連する別の手法と比較したうえでRDBを選んだ
☐ 落とし穴(このページの ⚠️ セクション)に該当しないか確認した
☐ 関連グループ教材で全体像と位置付けを把握した
📖 さらに学ぶには
本サイト内
論文一覧に戻る — リレーショナルデータベース を実際に使った再現論文をハンズオン形式で読む
このページ上部の「🔗 関連用語」から派生概念へ
「📚 関連グループ教材」で横断的な学習教材へ
外部リソース
scikit-learn 公式ドキュメント — 標準実装と例
StatQuest with Josh Starmer (YouTube) — 直感的な統計/ML 解説
Cross Validated (Stack Exchange) — 統計/ML の質問サイト
arXiv — 最新の手法論文プレプリント
困ったときは
データの可視化(散布図、 ヒストグラム、 箱ひげ図)で異常を確認
サンプルサイズ・欠損・外れ値を確認
仮定が満たされているか診断(正規性検定、 等分散性検定など)
類似研究での標準的な手法を確認
結果を複数手法でクロスチェック(頑健性確認)
🔗 同カテゴリの他用語
📚 参考文献・出典
🌟 拡張ハンドブック
📜 RDB の歴史と Codd の 12 原則
RDB は Edgar F. Codd (IBM 研究員)が 1970 年に Communications of the ACM に発表した論文「A Relational Model of Data for Large Shared Data Banks」が起源です。 当時主流だった階層型・ネットワーク型 DB の「データと処理の密結合」 という欠点を解消すべく、 集合論と関係代数 に基づく抽象モデルを提唱しました。 1979 年に Oracle が商用 RDBMS をリリースし、 1980 年代に DB2、 SQL Server、 PostgreSQL、 MySQL が登場、 現在に至るまで 50 年以上 RDB は基幹 DB の標準として君臨しています。
Codd の 12 原則(要約)
情報原則 :データは表の中の値だけで表現される
保証アクセス :表名・列名・主キーで全データにアクセス可能
NULL の系統的扱い :欠損は NULL で一貫表現
能動的カタログ :メタデータも表で管理
包括的サブ言語 :データ定義・操作・整合性・権限が 1 言語(=SQL)で記述
ビュー更新 :ビュー経由でも更新可能
高水準の挿入・更新・削除 :集合演算で複数行を一括処理
物理データ独立性 :ストレージ実装変更がアプリに影響しない
論理データ独立性 :論理スキーマ変更がアプリに影響しない
整合性独立性 :制約はスキーマで管理
分散独立性 :DB が分散しても透明
非破壊規則 :低水準 API で 12 原則を回避できない
🔑 主キー・外部キー・候補キーの違い
キー 定義 SSDSE-B の例 数量
スーパーキー 一意性を持つ列の組 (年度, 地域コード, 都道府県) 複数あり
候補キー スーパーキーで真部分集合がない最小単位 (年度, 地域コード), (年度, 都道府県) 複数あり
主キー(PK) 候補キーから 1 つ選んだもの (年度, 地域コード) 1 つ
外部キー(FK) 他表の主キーを参照する列 地域コード → 都道府県マスタ 任意の数
代替キー PK 以外の候補キー (年度, 都道府県) 任意
代理キー 人工的に振った ID(自然キーに代わる) AUTO_INCREMENT id 通常 1 つ
📐 正規化の段階詳細
第 1 正規形(1NF)
「各セルは原子値のみ」「繰り返し列なし」を満たす。 SSDSE-B-2026 はもともとリレーショナル形式で配布されているので 1NF を満たしている。 逆に Excel で「2010, 2011, ..., 2023」と各年度を列にしている 場合は非 1NF で、 pd.melt で Long 形式に変換すると 1NF 化できる。
第 2 正規形(2NF)
1NF を満たし、 かつ「非キー属性が候補キー全体に関数従属」する。 たとえば SSDSE-B の (年度, 地域コード) を主キーにした場合、 都道府県名は「地域コード」のみに依存(年度に依存しない)ので 2NF 違反。 解消するには「都道府県マスタ(地域コード, 都道府県名)」と「統計事実テーブル」に分割。
第 3 正規形(3NF)
2NF を満たし、 かつ「非キー属性間の関数従属」がない。 推移的関数従属(A→B→C で A→C)を排除。 SSDSE-B-2026 を分割した後の「都道府県マスタ(地域コード→都道府県→地方区分)」では、 地方区分は地域コード→都道府県→地方区分の推移依存なので 3NF 違反。 「都道府県マスタ」と「地方区分マスタ」に分割するのが 3NF。
BCNF・4NF・5NF
BCNF(Boyce-Codd 正規形) :3NF を強化、 非自明な関数従属の左辺が必ずスーパーキー。 4NF :多値従属を排除。 5NF :結合従属を排除。 実務では 3NF か BCNF で十分なケースがほとんど。
🔄 ACID と トランザクション
A: Atomicity(原子性)
「複数の操作をまとめて、 全成功か全失敗のどちらか」。 銀行送金で「A 口座から引いて B 口座に入れる」途中で停電しても、 両方が完了するか両方が元に戻る。 SQL では BEGIN; ... COMMIT; または ROLLBACK;。
C: Consistency(整合性)
「トランザクション後も DB 制約を満たす」。 NOT NULL、 UNIQUE、 CHECK、 FK の各制約が常に成立。
I: Isolation(隔離性)
「並行する複数トランザクションが互いに干渉しない」。 ANSI 標準で 4 レベル:READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE。 高いほど安全だが遅い。
D: Durability(永続性)
「COMMIT したデータは障害があっても消えない」。 WAL(Write-Ahead Logging)と fsync で保証。
⚡ インデックスの種類と選び方
インデックス 構造 適用 非適用
B-tree 平衡多分木 範囲検索、 順序検索、 等価 全文検索
ハッシュ ハッシュ表 等価検索のみ 範囲検索
GIN 転置インデックス JSON、 配列、 全文 高頻度更新
GiST 一般化検索木 地理空間、 範囲型 単純等価
BRIN ブロック範囲 大規模時系列 小規模
部分インデックス WHERE 条件付き 特定条件の高速化 汎用
💻 PostgreSQL vs MySQL vs SQLite
項目 PostgreSQL MySQL SQLite
ライセンス PostgreSQL(BSD 系) GPL/商用デュアル パブリックドメイン
構成 サーバープロセス サーバープロセス 単一ファイル
SQL 標準 ◎ ほぼ完全準拠 ○ 一部独自 ○ 一部省略
並行性 MVCC MVCC(InnoDB) 書込みは排他
スケール 数 TB 数 TB 数 GB(推奨)
複雑なクエリ ◎ ウィンドウ関数、 CTE、 JSON ○ MySQL 8 で改善 ○ 3.25+ でウィンドウ関数
SSDSE-B での適性 ○ 本格運用 ○ Web 連携 ◎ 学習・ハンズオン
❓ FAQ・20 問
Q1: NULL = NULL は TRUE になる?
A: ならない。 結果は UNKNOWN。 NULL の比較は IS NULL / IS NOT NULL。
Q2: COUNT(*) と COUNT(列) の違い
A: COUNT(*) は全行数、 COUNT(列) は NULL を除いた行数。
Q3: WHERE と HAVING の違い
A: WHERE は集計前、 HAVING は集計後の条件。 GROUP BY を伴うときに使い分け。
Q4: INNER JOIN と LEFT JOIN の使い分け
A: INNER は両方にマッチする行、 LEFT は左表の全行 + マッチする右表の行(マッチしない場合 NULL)。
Q5: サブクエリと CTE の違い
A: 結果はほぼ同じ。 CTE(WITH)は可読性が高く、 再帰可能。
Q6: インデックスは万能?
A: 読込みは速くなるが、 書込みは遅くなる。 また小さなテーブルでは効果が薄い。 数百〜数千行ならインデックス不要。
Q7: SELECT * はなぜ非推奨?
A: (1) 不要な列を返してネットワーク負荷増 (2) スキーマ変更で挙動が変わる (3) インデックスのカバリングが効かない。
Q8: ストアドプロシージャは使うべき?
A: パフォーマンスとセキュリティの観点ではあり。 ただしロジックが DB に閉じ込められる のでテスト・バージョン管理が難しい。
Q9: 主キーは複合と単独どちらが良い?
A: 自然キーが複合なら複合 PK、 そうでなければ AUTO_INCREMENT の代理キー(surrogate key)が運用しやすい。
Q10: UUID 主キーのメリット・デメリット
A: メリット:複数 DB 跨ぎでも衝突しない、 推測困難。 デメリット:B-tree インデックスの局所性が悪く、 INSERT 性能が低下。
Q11: SQL インジェクション対策
A: 必ずパラメータ化クエリ (プリペアドステートメント)。 文字列結合で SQL を組み立てない。
Q12: NoSQL は RDB に取って代わる?
A: NO。 用途次第。 整合性・関係性が重要なら RDB、 大規模・スキーマレスなら NoSQL。 多くは併用(polyglot persistence)。
Q13: VIEW のメリット
A: (1) 複雑なクエリを名前付き保存 (2) 権限制御 (3) スキーマ変更の隠蔽。 ただしマテリアライズドビュー以外は実体なし。
Q14: トランザクション分離レベルはどれを使う?
A: 多くは READ COMMITTED で十分。 金融処理は REPEATABLE READ や SERIALIZABLE。
Q15: パフォーマンスチューニングはどこから?
A: (1) EXPLAIN で実行計画確認 (2) インデックス追加 (3) JOIN 順序 (4) 統計情報更新 (5) スキーマ見直し。
Q16: SSDSE-B-2026 を SQLite にロードする最短コード
A: import sqlite3, pandas as pd; df = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932", skiprows=[1]); df.to_sql("ssdse_b", sqlite3.connect("ssdse.db"), index=False)。 列名は A1101 等の統計コードのまま格納される(日本語名で使うなら rename を挟む)。
Q17: ウィンドウ関数とは?
A: 集計しつつ各行も保持する関数。 SUM(x) OVER (PARTITION BY g ORDER BY t) など。 前年差・累積和・ランキングに必須。
Q18: ER 図を作るツールは?
A: dbdiagram.io、 draw.io、 PlantUML、 MySQL Workbench、 pgAdmin。 学習用は dbdiagram.io が最も手軽。
Q19: 分散 DB と単独 DB の使い分け
A: 単独で TB クラスまで扱えるので、 真に必要になるのは「単一サーバが処理しきれない」「複数地域に分散したい」場合のみ。
Q20: DuckDB と SQLite どちらが良い?
A: SQLite は OLTP(行指向)、 DuckDB は OLAP(列指向)に強い。 SSDSE-B のような分析用途は DuckDB が高速。
🏭 産業界の事例詳細(6 件・各 200 字超)
📘 ケース 1:銀行勘定系の RDB アーキテクチャ
三井住友・三菱 UFJ・みずほの 3 メガバンクの勘定系システムは、 すべて RDB(Oracle、 DB2)。 1 日 1,000 万件以上のトランザクション、 ACID 完全準拠で 99.999% の可用性を維持。
アーキテクチャ:(1) アクティブ-アクティブの 2 拠点構成、 (2) RAC(Real Application Clusters)で水平スケール、 (3) Data Guard で災害対策レプリカ、 (4) ストアドプロシージャで業務ロジック、 (5) パーティショニングで巨大表を分割。 「銀行は本当に NoSQL に移行しているか」という質問への答えは「補助的には、 中核は依然 RDB」。
📘 ケース 2:マイナンバー DB の設計思想
政府のマイナンバー DB は PostgreSQL ベース。 個人情報保護要件で、 (1) すべての参照履歴をログ表に記録、 (2) PK は 12 桁のマイナンバー、 (3) 各属性(住所、 氏名)は別テーブルに正規化、 (4) FK 制約で参照整合性を厳格管理、 (5) ロール(権限)ベースで列単位の SELECT 制御。
これは Codd の 12 原則を忠実に実装した好例で、 セキュリティ・整合性・トレーサビリティを同時に達成。 民間でも同様の設計が個人情報を扱う SaaS で標準化しつつあります。
📘 ケース 3:SSDSE-B-2026 を本格運用 DB に格納する練習
教育用として SQLite で十分ですが、 「本番運用ライク」な練習として PostgreSQL に格納するパターン:(1) CREATE TABLE ssdse_b (年度 INT, 地域コード TEXT, 都道府県 TEXT, 総人口 INT, ..., PRIMARY KEY (年度, 地域コード))、 (2) CREATE INDEX idx_prefecture ON ssdse_b(都道府県)、 (3) 都道府県マスタを別テーブルに分離、 (4) ビューで「人口上位 10」を保存。
ここまでやると、 「本物の RDB は SSDSE-B 規模では速度差を体感しづらい」「だが大量データになると JOIN・インデックスの効果が劇的」と理解できます。
📘 ケース 4:Salesforce / kintone の裏側の RDB
SaaS の代表格 Salesforce は、 Oracle 11i ベースのマルチテナント RDB 。 1 つの DB スキーマに数万社の顧客データが混在し、 「組織 ID」列で論理的に分離。
工夫点:(1) Custom Object は cf_custom_field_1, cf_custom_field_2, ... という汎用列で表現、 (2) シャーディングで地域別データセンターに分散、 (3) 自動チューニング・自動バックアップ。 この設計が「年間 100,000 ドルで全機能利用」を可能にしている根底。
📘 ケース 5:DuckDB の登場と SSDSE-B 解析
DuckDB(2019 年〜)は「インメモリ列指向 SQLite」と呼ばれ、 ローカル分析に特化。 SSDSE-B-2026.csv を直接 SQL で問い合わせ可能:SELECT Prefecture, A1101 FROM 'SSDSE-B-2026.csv' WHERE "SSDSE-B-2026"='2023'(生 CSV の列名は A1101 等の統計コード。 2 行目の日本語ヘッダ行の除去に注意)。
PostgreSQL と比べて:(1) インストール不要(pip install duckdb)、 (2) Parquet/CSV を SQL で直読み、 (3) 列指向で集計が高速、 (4) Python/R から zero-copy で連携。 SSDSE-B 規模のローカル分析では、 SQLite と DuckDB が 2 大ツール。
📘 ケース 6:RDB の限界とポリグロット永続化
現代の Web サービスは、 単一の RDB ではなく複数 DB の組合せ(polyglot persistence) が普通。 例:(1) ユーザー情報・注文 → PostgreSQL(ACID)、 (2) セッション・キャッシュ → Redis(高速 KVS)、 (3) ログ・分析 → Elasticsearch(全文検索)、 (4) レコメンド → Neo4j(グラフ DB)、 (5) BI 集計 → BigQuery(OLAP)。
SSDSE-B-2026 の分析でも、 (a) SQLite に元データ、 (b) DuckDB で集計、 (c) JSON ファイルで前処理結果、 と「目的別 DB」を併用する練習が現代的。
📚 50 連発レシピ集
🧪 50 連発の SQL レシピ(SSDSE-B-2026 を SQLite で)
SELECT * FROM ssdse_b WHERE 年度=2023; — フィルタ
SELECT 都道府県, 総人口 FROM ssdse_b WHERE 年度=2023 ORDER BY 総人口 DESC LIMIT 10; — TOP 10
SELECT 都道府県, AVG(総人口) FROM ssdse_b GROUP BY 都道府県; — 集計
SELECT COUNT(*) FROM ssdse_b; — 行数
SELECT MIN(総人口), MAX(総人口), AVG(総人口) FROM ssdse_b WHERE 年度=2023; — 統計
SELECT 年度, SUM(総人口) FROM ssdse_b GROUP BY 年度 ORDER BY 年度; — 年別合計
SELECT * FROM ssdse_b WHERE 都道府県 IN ('東京都','大阪府','愛知県'); — IN
SELECT * FROM ssdse_b WHERE 都道府県 LIKE '%県' AND 年度=2023; — LIKE
SELECT 都道府県 FROM ssdse_b WHERE 総人口 BETWEEN 1000000 AND 5000000; — BETWEEN
SELECT DISTINCT 年度 FROM ssdse_b; — ユニーク
SELECT 都道府県, 総人口, RANK() OVER (PARTITION BY 年度 ORDER BY 総人口 DESC) FROM ssdse_b; — ウィンドウ
SELECT 都道府県, LAG(総人口, 1) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS 前年 FROM ssdse_b;
SELECT 都道府県, 総人口 - LAG(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS 増減 FROM ssdse_b;
SELECT 都道府県, SUM(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS 累計 FROM ssdse_b;
SELECT 年度, AVG(総人口) OVER (ORDER BY 年度 ROWS 2 PRECEDING) AS 移動平均 FROM ssdse_b;
WITH 集計 AS (SELECT 都道府県, AVG(総人口) AS 平均 FROM ssdse_b GROUP BY 都道府県) SELECT * FROM 集計 WHERE 平均 > 1000000; — CTE
SELECT a.都道府県, a.総人口 - b.総人口 AS 増減 FROM ssdse_b a JOIN ssdse_b b ON a.地域コード=b.地域コード WHERE a.年度=2023 AND b.年度=2012; — 自己結合
SELECT 都道府県, 総人口, CASE WHEN 総人口 > 5000000 THEN '大' ELSE '小' END FROM ssdse_b; — CASE
SELECT 都道府県 FROM ssdse_b WHERE 年度=2023 AND 総人口 > (SELECT AVG(総人口) FROM ssdse_b WHERE 年度=2023); — サブクエリ
SELECT 都道府県, COUNT(*) FROM ssdse_b GROUP BY 都道府県 HAVING COUNT(*) > 10; — HAVING
CREATE INDEX idx_year ON ssdse_b(年度); — インデックス
CREATE INDEX idx_pref ON ssdse_b(都道府県);
EXPLAIN QUERY PLAN SELECT * FROM ssdse_b WHERE 年度=2023; — 実行計画
CREATE VIEW v_2023 AS SELECT * FROM ssdse_b WHERE 年度=2023; — ビュー
BEGIN; UPDATE ssdse_b SET 総人口=総人口+1; ROLLBACK; — トランザクション
SELECT 都道府県, 出生数*1000.0/総人口 AS 出生率 FROM ssdse_b WHERE 年度=2023 ORDER BY 出生率 DESC;
SELECT 都道府県, SUM(出生数) - SUM(死亡数) AS 自然増減 FROM ssdse_b GROUP BY 都道府県;
SELECT 都道府県, NTILE(4) OVER (ORDER BY 総人口) AS 4分位 FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, ROW_NUMBER() OVER (ORDER BY 総人口 DESC) FROM ssdse_b WHERE 年度=2023;
SELECT * FROM ssdse_b WHERE 都道府県 IS NOT NULL; — NULL 除外
SELECT 都道府県, COALESCE(出生数, 0) FROM ssdse_b; — COALESCE
SELECT printf('%d人', 総人口) FROM ssdse_b LIMIT 5; — フォーマット
SELECT date('now') AS today; — 日付
SELECT julianday('2024-01-01') - julianday('2023-01-01'); — 日数差
SELECT length(都道府県) FROM ssdse_b LIMIT 3; — 長さ
SELECT upper(都道府県), lower(都道府県) FROM ssdse_b LIMIT 3; — 大小
SELECT substr(都道府県, 1, 2) FROM ssdse_b LIMIT 5; — 部分文字列
SELECT trim(' hello '); — トリム
SELECT replace(都道府県, '県', '') FROM ssdse_b; — 置換
SELECT cast(総人口 AS REAL) / 10000 FROM ssdse_b; — キャスト
SELECT abs(-5), round(3.14159, 2), random() % 100; — 数値関数
ATTACH DATABASE 'other.db' AS o; — DB アタッチ
SELECT * FROM main.ssdse_b UNION ALL SELECT * FROM o.ssdse_b;
CREATE TABLE 都道府県マスタ (地域コード TEXT PRIMARY KEY, 名前 TEXT);
INSERT INTO 都道府県マスタ VALUES ('R01000', '北海道');
SELECT a.*, m.名前 FROM ssdse_b a JOIN 都道府県マスタ m ON a.地域コード=m.地域コード;
UPDATE ssdse_b SET 都道府県='東京' WHERE 都道府県='東京都';
DELETE FROM ssdse_b WHERE 年度 < 2015;
VACUUM; — 領域整理
PRAGMA table_info(ssdse_b); — スキーマ確認
📘 体系的解説(拡張版)
📜 RDB の歴史的展開
前史:階層型 DB とネットワーク型 DB(1960 年代)
RDB 登場以前は IBM の IMS(階層型、 1968)や CODASYL DBTG モデル(ネットワーク型)が主流。 これらは「ポインタで構造を表現」するため、 データ構造とアプリケーションコードが密結合。 構造変更のたびにアプリ全体を書き直す必要があり、 保守コストが膨大でした。
RDB の誕生:Codd 1970
Edgar F. Codd (IBM サンノゼ研究所)が 1970 年 6 月、 Communications of the ACM に「A Relational Model of Data for Large Shared Data Banks」を発表。 集合論と関係代数に基づき、 「データを論理的に表現し、 物理的実装と切り離す」設計を提唱。 これにより、 ストレージ実装やデータ構造を変えてもアプリは無修正、 という革命的な独立性が実現しました。
商用化:1979-1989
1979 年 :Larry Ellison が Oracle V2 をリリース(実は内部バージョン 1 は未完成)。 IBM の SQL/DS(1981)、 DB2(1983)、 Sybase(1984)と続く。 1986 年 :SQL が ANSI 標準(X3.135-1986)として制定。 1989 年 :PostgreSQL のオリジナル「POSTGRES」が UC Berkeley でリリース。
OSS RDB の勃興:1990 年代
MySQL(1995、 Monty Widenius)、 PostgreSQL(1996、 Berkeley から派生)、 SQLite(2000、 D. Richard Hipp)が登場。 LAMP スタック(Linux + Apache + MySQL + PHP)がインターネット黎明期の Web 開発の主流に。 「商用 RDB を買わずに済む」時代の到来。
NoSQL の挑戦:2009 年〜
2009 年に「NoSQL」という言葉が流行し、 MongoDB、 Cassandra、 Redis、 Neo4j など多様な DB が登場。 「RDB の終焉」が叫ばれましたが、 結局は用途別に使い分ける 「polyglot persistence」が定着。 RDB は依然として基幹系・帳簿系の中核。
NewSQL とクラウド DB:2015 年〜
Google Spanner(2012 発表、 2017 公開)、 CockroachDB、 TiDB など、 「ACID + 水平スケール」を両立する NewSQL が登場。 また Snowflake、 BigQuery など分析特化のクラウド DWH も普及。 オンプレ DBA からクラウド DBA への業務シフトが進行中。
📐 関係代数演算の完全ガイド
基本演算(5 個)
演算 記号 SQL 説明
選択 $\sigma_\theta(R)$ WHERE 条件 $\theta$ を満たす行を抽出
射影 $\pi_A(R)$ SELECT 指定列のみ取り出し
和 $R \cup S$ UNION 同じスキーマの 2 関係を結合
差 $R - S$ EXCEPT $R$ にあって $S$ にない行
直積 $R \times S$ CROSS JOIN 行の全組合せ
派生演算(4 個)
演算 記号 SQL 説明
交差 $R \cap S$ INTERSECT 両方にある行($R - (R - S)$)
θ 結合 $R \bowtie_\theta S$ INNER JOIN ON 条件 $\theta$ で結合($\sigma_\theta(R \times S)$)
自然結合 $R \bowtie S$ NATURAL JOIN 同名列で結合
商 $R \div S$ 複雑なサブクエリ 「$S$ のすべての値と組合せて $R$ にある」行
拡張演算(集合・集約)
演算 SQL SSDSE-B での例
集約 GROUP BY ... SUM/AVG/COUNT 年度別の総人口合計
外部結合 LEFT/RIGHT/FULL JOIN マスタにない地域も保持
ウィンドウ RANK/SUM OVER 都道府県内ランキング
再帰 WITH RECURSIVE 組織階層・地理階層
⚙️ SQL 標準の歴史
標準 年 主な機能
SQL-86 1986 初版、 基本 DML/DDL
SQL-89 1989 参照整合性
SQL-92 1992 外部結合、 サブクエリ拡張
SQL:1999 1999 CTE、 再帰、 オブジェクト型
SQL:2003 2003 ウィンドウ関数、 XML
SQL:2008 2008 MERGE、 TRUNCATE
SQL:2011 2011 時間データ、 範囲型
SQL:2016 2016 JSON、 多形態配列
SQL:2023 2023 グラフ SQL/PGQ、 ML 関数
🔧 SSDSE-B-2026 を題材にした完全 SQL 演習
演習 1:データロード
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A1302(15~64歳人口) A1303(65歳以上人口) A4101(出生数) A4200(死亡数)
北海道 5,092,000 2,897,000 1,681,000 24,430 75,120
東京都 14,086,000 9,368,000 3,205,000 86,348 137,241
沖縄県 1,468,000 882,000 350,000 12,549 15,110
…(全 47 行)
📋 コピー import sqlite3 , pandas as pd
df = pd . read_csv ( 'data/raw/SSDSE-B-2026.csv' , encoding = 'cp932' , skiprows = [ 1 ])
df = df . rename ( columns = { 'SSDSE-B-2026' : '年度' , 'Code' : '地域コード' ,
'Prefecture' : '都道府県' , 'A1101' : '総人口' ,
'A4101' : '出生数' , 'A4200' : '死亡数' ,
'A1303' : '高齢人口' , 'A1302' : '生産年齢人口' })
conn = sqlite3 . connect ( 'ssdse.db' )
df . to_sql ( 'ssdse_b' , conn , if_exists = 'replace' , index = False )
演習 2:基本クエリ 10 連発
-- 1. 全件数
SELECT COUNT(*) FROM ssdse_b;
-- 2. 年度のユニーク
SELECT DISTINCT 年度 FROM ssdse_b ORDER BY 年度;
-- 3. 都道府県のユニーク
SELECT DISTINCT 都道府県 FROM ssdse_b ORDER BY 都道府県;
-- 4. 2023 年の総人口上位 10
SELECT 都道府県, 総人口 FROM ssdse_b
WHERE 年度=2023 ORDER BY 総人口 DESC LIMIT 10;
-- 5. 2023 年の平均人口
SELECT AVG(総人口) FROM ssdse_b WHERE 年度=2023;
-- 6. 出生率 上位 5
SELECT 都道府県, 出生数*1000.0/総人口 AS 出生率
FROM ssdse_b WHERE 年度=2023
ORDER BY 出生率 DESC LIMIT 5;
-- 7. 高齢化率 上位 5
SELECT 都道府県, 高齢人口*1.0/総人口 AS 高齢化率
FROM ssdse_b WHERE 年度=2023
ORDER BY 高齢化率 DESC LIMIT 5;
-- 8. 自然増減(出生数-死亡数)
SELECT 都道府県, 出生数 - 死亡数 AS 自然増減
FROM ssdse_b WHERE 年度=2023
ORDER BY 自然増減 DESC;
-- 9. 都道府県別の総人口平均
SELECT 都道府県, AVG(総人口) FROM ssdse_b GROUP BY 都道府県;
-- 10. 年度別の全国合計
SELECT 年度, SUM(総人口) FROM ssdse_b GROUP BY 年度;
演習 3:ウィンドウ関数
-- 都道府県内での前年差
SELECT 年度, 都道府県, 総人口,
総人口 - LAG(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS 前年差
FROM ssdse_b;
-- 各年度のランキング
SELECT 年度, 都道府県, 総人口,
RANK() OVER (PARTITION BY 年度 ORDER BY 総人口 DESC) AS 順位
FROM ssdse_b;
-- 3 年移動平均
SELECT 年度, 都道府県, 総人口,
AVG(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度 ROWS 2 PRECEDING) AS 移動平均
FROM ssdse_b;
演習 4:自己結合
-- 2012 年度と 2023 年度の比較
SELECT a.都道府県,
a.総人口 AS 人口_2023,
b.総人口 AS 人口_2012,
a.総人口 - b.総人口 AS 増減,
ROUND((a.総人口 - b.総人口) * 100.0 / b.総人口, 2) AS 増減率
FROM ssdse_b a
JOIN ssdse_b b ON a.地域コード=b.地域コード
WHERE a.年度=2023 AND b.年度=2012
ORDER BY 増減率 DESC;
🎯 RDB 設計のベストプラクティス
主キーは必ず付ける :行を一意に識別する手段がないと、 後でデータ整合性が破綻。
命名規則を統一 :snake_case か camelCase。 表は単数形(user)か複数形(users)。 1 つの規約を貫く。
NULL 許可は最小に :NULL は「未知」「未入力」「適用外」など曖昧。 デフォルト値を入れるか、 NOT NULL 制約を。
外部キーは必ず張る :参照整合性の DB レベル保証。 「実装時に厄介」と言って外すと後悔する。
インデックスは慎重に :読込みは速くなるが書込みは遅くなる。 EXPLAIN で実効性を確認。
3NF を基本に :4NF・5NF は実務では稀。 ただし分析系では適度な非正規化(スター/スノーフレークスキーマ)。
マイグレーション管理 :Flyway、 Liquibase、 Alembic などでスキーマ変更を履歴管理。
バックアップ自動化 :日次フル + 1 時間ごとの差分が標準。
監視 :スロークエリログ、 接続数、 ロック状態を Prometheus + Grafana で可視化。
テスト DB :本番と同じスキーマ・サイズのテスト環境を用意。
🎯 実践演習・チェックリスト
🎓 RDB 100 ステップ学習プラン
第 1 段階:基本(ステップ 1-20)
SQLite のインストール(Python に標準同梱)
SSDSE-B-2026 を pandas で読込
SQLite DB ファイル作成
to_sql でテーブル作成
SELECT * FROM ssdse_b LIMIT 5
SELECT COUNT(*) FROM ssdse_b
SELECT DISTINCT 都道府県 FROM ssdse_b
WHERE 年度=2023
WHERE 都道府県='東京都'
WHERE 総人口 > 5000000
ORDER BY 総人口 DESC
LIMIT 10
GROUP BY 年度
SUM、 AVG、 MIN、 MAX、 COUNT
HAVING COUNT(*) > 10
WHERE 都道府県 LIKE '%県'
WHERE 都道府県 IN ('東京都', '大阪府')
WHERE 総人口 BETWEEN 1000000 AND 5000000
WHERE 都道府県 IS NOT NULL
SELECT 都道府県, 総人口/10000 AS 万人 FROM ssdse_b
第 2 段階:結合(ステップ 21-40)
都道府県マスタテーブル作成
INSERT INTO で行追加
INNER JOIN の基本
LEFT JOIN
RIGHT JOIN
FULL OUTER JOIN(SQLite では UNION で代替)
CROSS JOIN
自己結合(前年比較)
サブクエリ(WHERE 句内)
サブクエリ(FROM 句内)
サブクエリ(SELECT 句内)
EXISTS / NOT EXISTS
IN / NOT IN サブクエリ
UNION
UNION ALL
INTERSECT
EXCEPT(SQLite では EXCEPT)
CASE 式
COALESCE で NULL 処理
NULLIF
第 3 段階:ウィンドウ関数(ステップ 41-60)
ROW_NUMBER() OVER
RANK() OVER
DENSE_RANK() OVER
PARTITION BY
ORDER BY 内
LAG(前期参照)
LEAD(後期参照)
FIRST_VALUE / LAST_VALUE
SUM() OVER(累積)
AVG() OVER ROWS BETWEEN(移動平均)
NTILE(パーセンタイル)
PERCENT_RANK
CUME_DIST
RANGE 指定
ROWS BETWEEN UNBOUNDED PRECEDING
名前付きウィンドウ(WINDOW 句)
複数ウィンドウの組合せ
ウィンドウとサブクエリの比較
ウィンドウのパフォーマンス
EXPLAIN で実行計画確認
第 4 段階:DDL と制約(ステップ 61-80)
CREATE TABLE
データ型(INT、 REAL、 TEXT、 BLOB)
PRIMARY KEY
FOREIGN KEY
UNIQUE 制約
NOT NULL 制約
CHECK 制約
DEFAULT 値
AUTO_INCREMENT(SQLite では INTEGER PRIMARY KEY)
CREATE INDEX
DROP INDEX
ALTER TABLE ADD COLUMN
ALTER TABLE RENAME
DROP TABLE
CREATE VIEW
DROP VIEW
CREATE TRIGGER
BEGIN / COMMIT / ROLLBACK
SAVEPOINT
VACUUM(領域整理)
第 5 段階:高度な操作(ステップ 81-100)
CTE(WITH 句)
WITH RECURSIVE
階層クエリ(祖先・子孫)
ピボット(SUM + CASE)
アンピボット(UNION ALL)
JSON 関数(json_extract)
正規表現(REGEXP)
FTS(全文検索)
R-tree(地理空間)
ATTACH DATABASE(複数 DB)
pragma の調整
WAL モード
バックアップ(BACKUP コマンド)
レプリケーション(litestream)
EXPLAIN QUERY PLAN
ANALYZE で統計情報
仮想テーブル
カスタム関数(Python から)
セッション/エクステンション拡張
本番運用ベストプラクティス
📊 SSDSE-B-2026 を題材にした実用 SQL レシピ 30 連発
SELECT 都道府県, 総人口 FROM ssdse_b WHERE 年度=2023 ORDER BY 総人口 DESC LIMIT 10;
SELECT 都道府県, AVG(総人口) FROM ssdse_b GROUP BY 都道府県;
SELECT 年度, SUM(総人口) FROM ssdse_b GROUP BY 年度;
SELECT 都道府県, 出生数*1000.0/総人口 AS 出生率 FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, 高齢人口*1.0/総人口 AS 高齢化率 FROM ssdse_b WHERE 年度=2023 ORDER BY 高齢化率 DESC;
SELECT 都道府県, RANK() OVER (ORDER BY 総人口 DESC) FROM ssdse_b WHERE 年度=2023;
SELECT 年度, 都道府県, 総人口 - LAG(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS 増減 FROM ssdse_b;
SELECT 都道府県, MIN(総人口) AS 最小, MAX(総人口) AS 最大 FROM ssdse_b GROUP BY 都道府県;
SELECT a.都道府県, a.総人口, b.総人口 AS 前年 FROM ssdse_b a JOIN ssdse_b b ON a.地域コード=b.地域コード AND a.年度=b.年度+1;
WITH 増減 AS (SELECT 都道府県, 年度, 総人口, 総人口 - LAG(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS d FROM ssdse_b) SELECT * FROM 増減 WHERE d < 0;
SELECT 都道府県, AVG(総人口) OVER (PARTITION BY 都道府県 ORDER BY 年度 ROWS 2 PRECEDING) AS 3年平均 FROM ssdse_b;
SELECT 都道府県 FROM ssdse_b WHERE 年度=2023 AND 総人口 > (SELECT AVG(総人口) FROM ssdse_b WHERE 年度=2023);
SELECT 都道府県, COUNT(*) FROM ssdse_b GROUP BY 都道府県 HAVING COUNT(*) = 12;
CREATE INDEX idx_year_pref ON ssdse_b(年度, 都道府県);
EXPLAIN QUERY PLAN SELECT * FROM ssdse_b WHERE 年度=2023 AND 都道府県='東京都';
CREATE VIEW v_2023_summary AS SELECT 都道府県, 総人口, 出生数, 死亡数 FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, CASE WHEN 総人口 > 5000000 THEN '大' WHEN 総人口 > 1500000 THEN '中' ELSE '小' END AS 規模 FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, NTILE(4) OVER (ORDER BY 総人口) AS 4分位 FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, ROUND(出生数*1000.0/総人口, 2) AS 出生率_per1000 FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, COALESCE(死亡数, 0) FROM ssdse_b WHERE 年度=2023;
SELECT 都道府県, SUM(出生数) OVER (PARTITION BY 都道府県 ORDER BY 年度) AS 累計出生数 FROM ssdse_b;
CREATE TABLE 地方区分 (地域コード TEXT PRIMARY KEY, 地方名 TEXT);
INSERT INTO 地方区分 VALUES ('R01000', '北海道'), ('R02000', '東北'), ...;
SELECT g.地方名, AVG(s.総人口) FROM ssdse_b s JOIN 地方区分 g ON s.地域コード=g.地域コード GROUP BY g.地方名;
BEGIN; UPDATE ssdse_b SET 都道府県='東京都' WHERE 都道府県='東京'; COMMIT;
-- PostgreSQL のみ(SQLite に CORR は無い): SELECT ROUND(CORR(総人口, 出生数)::numeric, 3) FROM ssdse_b WHERE 年度=2023;
SELECT json_object('都道府県', 都道府県, '人口', 総人口) FROM ssdse_b WHERE 年度=2023;
PRAGMA table_info(ssdse_b);
PRAGMA index_list(ssdse_b);
VACUUM;
📚 包括的付録
📖 包括的付録:RDBの完全ガイド
本付録は、 RDBを初めて学ぶ人から、 実務でフル活用する人まで、 段階的に深掘りできるよう構成されています。 すべての例は SSDSE-B-2026 を使った実コードで、 そのままコピペで動作確認できます。
D.1 SQL 関数 完全リファレンス
D.1.1 集約関数
関数 説明 SQLite PostgreSQL MySQL
COUNT(*) 全行数 ○ ○ ○
COUNT(列) NULL を除いた行数 ○ ○ ○
COUNT(DISTINCT 列) ユニーク数 ○ ○ ○
SUM(列) 合計 ○ ○ ○
AVG(列) 平均 ○ ○ ○
MIN(列) 最小 ○ ○ ○
MAX(列) 最大 ○ ○ ○
STDDEV(列) 標準偏差 ×(拡張) ○ ○
VARIANCE(列) 分散 ×(拡張) ○ ○
GROUP_CONCAT(列) 文字列連結 ○ STRING_AGG ○
PERCENTILE_CONT(0.5) 中央値 × ○ 8.0+
CORR(x, y) 相関係数 × ○ ×
D.1.2 ウィンドウ関数
関数 説明 使用例
ROW_NUMBER() 連番 行番号付与
RANK() 順位(同順位あり) 人口ランキング
DENSE_RANK() 順位(連番) 同位含む順位
LAG(列, n) 前 n 期の値 前年比較
LEAD(列, n) 後 n 期の値 将来予測
FIRST_VALUE 最初の値 基準値からの差
LAST_VALUE 最後の値 最新値
NTILE(n) n 等分位 四分位分類
PERCENT_RANK() 百分位 パーセンタイル
CUME_DIST() 累積分布 CDF 計算
D.1.3 文字列関数
関数 説明
LENGTH(s) 文字数
UPPER(s), LOWER(s) 大小変換
SUBSTR(s, start, len) 部分文字列
TRIM(s), LTRIM(s), RTRIM(s) トリム
REPLACE(s, old, new) 置換
CONCAT(a, b) or 'a' || 'b' 結合
INSTR(s, find) 位置検索
LIKE / GLOB パターンマッチ
D.1.4 日付関数(SQLite)
date('now') -- 今日
datetime('now') -- 現在日時
strftime('%Y', date) -- 年抽出
strftime('%m', date) -- 月抽出
date('now', '-1 year') -- 1 年前
date('2023-01-01', '+30 days') -- 30 日後
julianday(date1) - julianday(date2) -- 日数差
D.2 索引設計のガイドライン
主キーには自動でインデックスが張られる (B-tree)
外部キー列もインデックスを推奨 (結合性能向上)
WHERE 句で頻繁に使う列 にインデックス
ORDER BY、 GROUP BY で使う列 にもインデックス検討
カーディナリティが低い列(例:性別) はインデックスより効果が薄い
複合インデックスは「前方一致」で効く :(年度, 都道府県) なら 年度 検索は OK、 都道府県単独はインデックス効かず
関数インデックス :CREATE INDEX ON ssdse_b(LOWER(都道府県))
部分インデックス :CREATE INDEX ON ssdse_b(総人口) WHERE 年度=2023
カバリングインデックス :SELECT に使う列すべてを含むインデックス(テーブル参照不要)
EXPLAIN で確認 :本当にインデックスが使われているか
🎁 追加リファレンス
📚 主要 RDBMS の比較とライセンス
RDBMS ライセンス 特徴 使用例
PostgreSQL PostgreSQL(BSD 系) SQL 標準準拠、 拡張豊富 Reddit、 Skype
MySQL GPL/商用 Web で人気 WordPress、 YouTube
MariaDB GPL MySQL の OSS 派生 Wikipedia、 Google
SQLite パブリックドメイン 単一ファイル スマホアプリ、 学習
Oracle Database 商用 エンタープライズ最高峰 銀行、 通信
SQL Server 商用 Microsoft 統合 .NET アプリ
IBM Db2 商用 メインフレーム連携 金融、 製造
Amazon Aurora 商用(クラウド) MySQL/PG 互換 AWS 利用者
Google Cloud SQL 商用(クラウド) マネージド GCP 利用者
CockroachDB BSL(OSS+商用) 分散・NewSQL Netflix、 Bose
🔧 SSDSE-B-2026 を使った 30 個のクエリパターン
2023 年の全データ:SELECT * FROM ssdse_b WHERE 年度=2023
都道府県数:SELECT COUNT(DISTINCT 都道府県) FROM ssdse_b
年度数:SELECT COUNT(DISTINCT 年度) FROM ssdse_b
東京の総人口推移:SELECT 年度, 総人口 FROM ssdse_b WHERE 都道府県='東京都' ORDER BY 年度
各年度の最大人口県:SELECT 年度, 都道府県, MAX(総人口) FROM ssdse_b GROUP BY 年度
2023 年の高齢化率トップ 5:SELECT 都道府県, 高齢人口*1.0/総人口 r FROM ssdse_b WHERE 年度=2023 ORDER BY r DESC LIMIT 5
2012-2023 の総人口変化率:WITH t1 AS (SELECT 地域コード, 総人口 FROM ssdse_b WHERE 年度=2012), t2 AS (SELECT 地域コード, 総人口 FROM ssdse_b WHERE 年度=2023) SELECT t1.地域コード, (t2.総人口-t1.総人口)*100.0/t1.総人口 AS 変化率 FROM t1 JOIN t2 USING(地域コード)
北海道・東京・沖縄の比較:SELECT 年度, 都道府県, 総人口, 出生数, 死亡数 FROM ssdse_b WHERE 都道府県 IN ('北海道','東京都','沖縄県') ORDER BY 都道府県, 年度
全国合計人口(年度別):SELECT 年度, SUM(総人口) FROM ssdse_b GROUP BY 年度 ORDER BY 年度
都道府県別 12 年平均人口:SELECT 都道府県, AVG(総人口) FROM ssdse_b GROUP BY 都道府県 ORDER BY 2 DESC
各都道府県のピーク年:SELECT s1.都道府県, s1.年度 FROM ssdse_b s1 WHERE s1.総人口 = (SELECT MAX(総人口) FROM ssdse_b s2 WHERE s2.都道府県=s1.都道府県)
2023 年に減少した都道府県:SELECT a.都道府県 FROM ssdse_b a JOIN ssdse_b b ON a.地域コード=b.地域コード WHERE a.年度=2023 AND b.年度=2022 AND a.総人口 < b.総人口
2023 年に増加した都道府県:上記の不等号を逆に
出生率トップ:SELECT 都道府県, 出生数/総人口*1000.0 r FROM ssdse_b WHERE 年度=2023 ORDER BY r DESC
死亡率トップ:SELECT 都道府県, 死亡数/総人口*1000.0 r FROM ssdse_b WHERE 年度=2023 ORDER BY r DESC
自然増減:SELECT 都道府県, 出生数-死亡数 FROM ssdse_b WHERE 年度=2023 ORDER BY 2 DESC
生産年齢人口(15〜64 歳、 A1302):SELECT 都道府県, 生産年齢人口 FROM ssdse_b WHERE 年度=2023
従属人口指数:SELECT 都道府県, (総人口-生産年齢人口)*100.0/生産年齢人口 FROM ssdse_b WHERE 年度=2023
2023 年の粗死亡率:SELECT 都道府県, 死亡数*1000.0/総人口 FROM ssdse_b WHERE 年度=2023
都市圏 vs 地方の比較:地方区分マスタを使って集計
気候別の家計消費:年平均気温 (B4101) で分類してから消費支出 (L3221) を集計
4 分位での分類:NTILE(4) OVER (ORDER BY 総人口)
百分位ランク:PERCENT_RANK()
移動平均:3 年・5 年
累積:SUM() OVER (PARTITION BY 都道府県 ORDER BY 年度)
差分:LAG で取得
成長率:(現在 − 前年) / 前年
CAGR(年平均成長率):POW(末期/初期, 1.0/(年数))-1
相関:Pearson 相関係数の計算
カバリングインデックスの確認:EXPLAIN QUERY PLAN
🔧 補足リソース
🔒 RDB セキュリティのベストプラクティス
SQL インジェクション対策 :パラメータ化クエリ必須
最小権限の原則 :アプリ用ユーザは SELECT/INSERT/UPDATE のみ
暗号化 :保存時(at rest)と通信時(in transit)両方
監査ログ :すべての DDL/DML を記録
バックアップ :日次フル + 1 時間ごと差分
パスワード管理 :環境変数、 シークレットマネージャ
接続プール :適切なサイズ設定
ファイアウォール :DB サーバへの直接アクセスを制限
VPC/プライベートネットワーク :パブリック IP を持たせない
定期的な脆弱性スキャン :CVE 情報の追跡
📈 RDB パフォーマンスチューニング体系
レベル 1:クエリ単位
EXPLAIN で実行計画確認
適切なインデックス追加
SELECT * 回避
WHERE 条件で先頭から絞る
JOIN の順序最適化
サブクエリ vs JOIN の選択
レベル 2:スキーマ単位
正規化と非正規化のバランス
パーティショニング(日付・地域)
マテリアライズドビュー
列指向 vs 行指向
シャーディング
レベル 3:インフラ単位
メモリ・CPU・ディスクの増強
SSD への移行
レプリケーション(読込み負荷分散)
キャッシュ層追加(Redis)
CDN(Web 配信時)
💼 RDB 関連職種とキャリアパス
職種 主要スキル 年収目安
DB エンジニア SQL、 設計、 チューニング 500-1,000 万円
DBA 運用、 監視、 セキュリティ 600-1,200 万円
データアーキテクト 設計、 戦略 900-1,800 万円
バックエンドエンジニア SQL、 ORM、 API 500-1,200 万円
SRE 運用、 自動化、 SLO 700-1,500 万円
データエンジニア DWH、 ETL、 ストリーミング 700-1,400 万円
📚 RDB 関連の主要書籍
C.J. Date『An Introduction to Database Systems』
Hector Garcia-Molina et al.『Database Systems: The Complete Book』
Stéphane Faroult『The Art of SQL』
Markus Winand『SQL Performance Explained』
Joe Celko『SQL for Smarties』
Adam Tornhill『SQL Antipatterns』
『達人に学ぶ DB 設計徹底指南書』ミック
『SQL アンチパターン』Bill Karwin
『PostgreSQL 徹底入門』近藤雄太郎
『MySQL によるタフな運用の極意』奥野幹也
📖 完全マスター・ハンドブック
📋 RDB ハンドブック:100 のヒント
主キーは必ず指定する
外部キー制約は最初から設定
NOT NULL を可能な限り適用
DEFAULT 値で NULL を回避
CHECK 制約で値の範囲を保証
UNIQUE 制約で重複防止
命名規則は snake_case を統一
テーブル名は複数形(users)か単数形(user)を統一
列名に予約語を避ける
カラム数は 30 以内が望ましい
1 トランザクション = 1 業務単位
BEGIN / COMMIT / ROLLBACK を明示
長時間トランザクションを避ける
デッドロックを意識(同じ順序でロック)
分離レベルは READ COMMITTED が基本
SELECT * を避ける
WHERE で必要な列だけを取得
JOIN の順序を意識(小テーブルから大テーブルへ)
EXISTS と IN の使い分け
サブクエリより CTE
UNION より UNION ALL(重複排除が不要なら)
HAVING より WHERE(集計前に絞り込み)
ORDER BY は最小限の列
LIMIT で行数制限
EXPLAIN で実行計画確認
インデックスは選択性が高い列
複合インデックスは前方一致
カバリングインデックスで SELECT 高速化
関数インデックスで WHERE 句の関数
部分インデックスで特定条件
テーブル統計を定期更新(ANALYZE)
VACUUM で領域整理
パーティショニングで巨大表を分割
シャーディングで水平スケール
レプリケーションで読込み負荷分散
マスター/スレーブ構成
マスター/マスター構成は要注意
バックアップは日次フル + 1 時間差分
WAL(Write-Ahead Logging)有効化
ロールバック可能な範囲を確保
パスワードは環境変数で管理
SQL インジェクション対策(パラメータ化クエリ)
暗号化(at rest、 in transit)
監査ログ(DDL、 DML 全件)
最小権限の原則
マイグレーション管理(Flyway、 Liquibase)
テスト DB と本番 DB の同期
本番への直接アクセスを制限
API 経由でのアクセスを推奨
監視(Prometheus、 Grafana)
スロークエリログを定期的に確認
接続数の上限を意識
接続プールサイズ調整
クエリタイムアウト設定
アプリケーション側のリトライロジック
キャッシュ層(Redis)の併用
マテリアライズドビューで集計高速化
ストアドプロシージャは慎重に
トリガーは副作用に注意
ビューは可読性のために活用
命名規則:テーブルは名詞
命名規則:列は具体的に
命名規則:インデックスは idx_ プレフィックス
命名規則:制約は ck_, fk_, uq_ プレフィックス
正規化は 3NF まで
非正規化は読込み中心なら検討
分析系は星型/雪型スキーマ
OLTP と OLAP を分離
ETL の頻度を業務に合わせる
データ品質チェック(DBT、 Great Expectations)
スキーマ進化は後方互換性を保つ
追加カラムは末尾に
列削除は段階的に
型変更は注意(NUMBER → VARCHAR は不可逆)
命名変更は別名 → 移行 → 削除
テーブル削除前にバックアップ
本番デプロイは平日の業務時間外
本番デプロイ前にステージング検証
ロールバックスクリプトを準備
ダウンタイム最小化(Blue/Green デプロイ)
SaaS DB(Supabase、 PlanetScale)の活用
クラウド DB(RDS、 Cloud SQL)でマネージド化
BigQuery、 Snowflake で分析特化
DuckDB でローカル分析
SQLite で組込み・テスト
PostgreSQL で OSS の標準
MySQL で Web の定番
Oracle で エンタープライズ最高峰
SQL Server で Microsoft 環境
NoSQL(MongoDB、 Cassandra)との使い分け
NewSQL(CockroachDB、 TiDB)の検討
グラフ DB(Neo4j)の利用
時系列 DB(InfluxDB、 TimescaleDB)
ベクトル DB(Pinecone、 Weaviate)
地理空間 DB(PostGIS)
JSON 対応(PostgreSQL、 MySQL 8)
全文検索(PostgreSQL FTS、 Elasticsearch)
レプリケーションラグの監視
障害復旧訓練(DR)の実施
キャパシティプランニング
クエリのパターン分析
n+1 問題を避ける(Eager Loading)
キャリア:DBA、 データエンジニア、 SRE
🌟 まとめと次のステップ
📚 RDB(リレーショナルデータベース)の総合チェックポイント
1. 基本理解
RDB(リレーショナルデータベース)を学ぶ上で、 最初に押さえるべき概念とその関係性。 SSDSE-B-2026 を題材に、 47 都道府県 × 12 年 × 112 指標のデータを使って実際に試すことで、 理論と実践のギャップを埋めることができます。 統計学・データサイエンスの教育では、 ハンズオン形式での学習が最も効果的とされており、 本ページもその設計思想に従って構成されています。
2. 応用力
基本を押さえたら、 実務での応用力を養います。 教科書通りの「綺麗なケース」ではなく、 SSDSE-B-2026 のような現実のデータには、 表記揺れ・欠損・外れ値・分布の偏りなど、 様々な「現実のノイズ」が含まれています。 これらに対処しながら意味のある分析結果を導く力こそが、 データサイエンティストとしての真価です。
3. 倫理と社会的責任
データ分析者には、 単なる技術的能力以上のものが求められます。 (1) プライバシー保護 :個人情報の取扱、 (2) バイアスへの自覚 :データに含まれる社会的偏見、 (3) 透明性 :分析手法と前提の開示、 (4) 再現可能性 :他者が同じ結論に到達できる文書化、 (5) 社会的影響 :分析結果が引き起こす意思決定の影響範囲。
4. 継続的な学習
データサイエンスは急速に進化する分野です。 新しい手法・ツール・データソースが次々と登場するため、 継続的な学習が不可欠。 (1) 論文購読 :arXiv の関連分野を週次でチェック、 (2) ライブラリ追跡 :scipy、 sklearn、 PyTorch のリリースノート、 (3) カンファレンス :NeurIPS、 ICML、 KDD、 PyData、 (4) コミュニティ :Twitter/X、 LinkedIn、 国内の勉強会、 (5) ハンズオン :Kaggle、 SIGNATE、 SSDSE コンペ。
5. 本ページから次のステップへ
RDB(リレーショナルデータベース)を理解したら、 次は関連概念へ。 本サイトの「関連用語」セクションから派生概念へ進み、 「関連グループ教材」で全体像を把握。 さらに「論文一覧」では、 統計データ分析コンペティションで実際に提出された 159 件の論文を、 ハンズオン形式で再現することができます。 ぜひ自分のテーマで、 SSDSE-B-2026 を使った独自の研究にも挑戦してみてください。
🔬 用語固有の具体例(10 個)
SSDSE-B-2026.csv を SQLite にロード:df.to_sql('ssdse_b', sqlite3.connect('db'), index=False)
主キー (年度, 地域コード) で複合キーを設定し、 一意性を保証
都道府県マスタを別テーブル化し、 外部キー制約で参照整合性を維持
2023 年の総人口上位 10 件を ORDER BY 総人口 DESC LIMIT 10 で取得
ウィンドウ関数 RANK() OVER (PARTITION BY 年度 ORDER BY 総人口 DESC) で各年の順位
自己結合で 2012 年度と 2023 年度の人口比較、 増減率を計算
CTE で複雑なクエリを段階的に組み立てる(WITH … AS)
B-tree インデックスを (年度) に張り、 EXPLAIN で実行計画確認
VIEW で「2023 年サマリ」を定義し、 利用側のクエリを簡潔化
DuckDB で CSV を直接クエリ:SELECT * FROM 'SSDSE-B-2026.csv'
🎯 SSDSE-B-2026 で学べること
SSDSE-B-2026 は単なるデータセットではなく、 「日本のデータサイエンス教育のための共通言語」 として設計されています。 47 都道府県という馴染みのある単位、 12 年間という適切な期間、 112 個という網羅的な指標。 すべての受講生が同じデータを扱うことで、 結果を比較・議論・批判できるという、 教育的価値を持っています。
本ページで紹介した RDB(リレーショナルデータベース)の理論と実装は、 SSDSE-B-2026 という共通の素材を介して、 全国の学習者・研究者・実務家が共有できる知識基盤となります。 学習を通じて、 ぜひ「自分の県」「自分の地域」「自分の興味」を切り口に、 オリジナルの分析にチャレンジしてみてください。
📞 困ったときのサポート
本サイト内 :用語集トップ → 概念マップ → 論文一覧
関連書籍 :本ページの参考文献セクションを参照
オンラインフォーラム :Stack Overflow、 Cross Validated、 teratail
公的サポート :統計センター、 e-Stat ヘルプデスク
勉強会 :PyData Tokyo、 R-bloggers、 JapanTUG
本ページの内容は、 統計・データサイエンスを学ぶすべての方の参考となれば幸いです。 RDB(リレーショナルデータベース)を深く理解し、 実務・研究・教育の様々な場面で活用してください。 ご質問・ご指摘がある場合は、 本リポジトリの GitHub Issues にお寄せください。
🗺 概念マップ
RDB(リレーショナルデータベース)と SQL・正規化・トランザクション(ACID)・主キー/外部キー・インデックス・NoSQL との対比を俯瞰する概念マップ。 構造化データ管理の基盤としての位置づけを示す。
RDB
前提: SQL / 正規化
並列: NoSQL / DWH
発展: ACID / JOIN
応用: PostgreSQL / SQLite
対比: CSV / pandas
統合: SQLAlchemy / ORM
SSDSE-B-2026 を SQLite に取り込み、 都道府県マスタテーブル (prefecture_code, name) と観測テーブル (year, prefecture_code, population, births) を JOIN すると、 「ある年の人口上位 10 県」 のような分析を SQL 1 文で書ける。
SELECT name, population FROM obs JOIN pref USING(prefecture_code) WHERE year=2022 ORDER BY population DESC LIMIT 10; — RDB が CSV や JSON に対して持つ「インデックス + JOIN + 制約」の優位性が、 47 都道府県 × 複数年のパネルで明確になる。
🔗 隣接手法への橋渡し
「リレーショナルデータベース」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。
SSDSE-B-2026 を題材にした リレーショナルデータベース の活用は、 上流 (取得・整形) と下流 (解釈・可視化) を含めて初めて完結する。
🌳 手法選択フロー
「リレーショナルデータベース」を実際の課題に当てはめるとき、 状況別に何を選ぶかを 3 段階で判定する。
データ構造は表形式か? Yes → RDB、 No (階層・グラフ) → NoSQL を検討
関係 (JOIN) を多用するか? Yes → RDB が得意、 No → KVS / Document でも可
整合性が最重要か? Yes → RDB の ACID、 No → BASE で高可用性を取る
SSDSE-B-2026 は CSV 1 枚で完結するが、 「県マスタ」「年マスタ」「指標マスタ」に分けて RDB に格納すると、 年代別比較や指標横断検索が SQL 1 行で書ける。
🎨 直感で掴む(追補)── 「表の集合+関係」で世界を写す
RDB の直感を一言でいえば 「事実を、 重複なく、 表に切り分けて置く」 ことである。 現実世界のデータには必ず「何が」「いつ」「どこで」「どれだけ」という軸がある。 RDB はこれを 1 つの巨大な表に押し込めず 、 テーマ別の表(テーブル)に分け、 表と表を キー でつなぐ。 SSDSE-B-2026 でいえば「都道府県という不変の属性」と「年ごとに変わる観測値」は別の性質を持つので、 別の表に置くのが自然である。
用語の対応表(直感 → RDB 用語)
日常の言い方 RDB 用語 SSDSE-B-2026 での具体
1 枚のシート テーブル(関係 relation) prefectures/facts
1 行のデータ レコード(タプル=行) 「北海道・2023・総人口・5,092,000」
列の見出し 属性(カラム) Prefecture/A1101
行の背番号 主キー(PRIMARY KEY) Code(地域コード)
別シートへの参照 外部キー(FOREIGN KEY) facts.pref_code → prefectures.pref_code
シートの引き当て JOIN(結合) 県名を付けて集計する
重複を無くす整理 正規化(normalization) 県名を prefectures に 1 回だけ持つ
キーが「関係」を作る :主キーは「この行はこれだ」と一意に指し示す背番号、 外部キーは「あの表のあの行を指す」矢印である。 この矢印が張られていれば、 存在しない相手を指す矢印(迷子の参照)を DB が拒否できる。 これが 参照整合性 であり、 表計算ソフトには無い RDB の核心的な安心感を生む(本ページ上部の 🎮 ウィジェットで、 C99 という存在しない顧客を指す注文がどう扱われるかを実際に動かせる)。
なぜ分けるのか=正規化の直感 :もし県名・地方区分・年ごとの観測値を全部 1 枚の表に並べると、 「北海道」という県名が観測のたびに何度も書かれる。 北海道の表記を「北海道地方」に直したくなったら 全行を書き換え ねばならず、 1 か所でも漏れれば「同じ県なのに名前が 2 種類」という矛盾(更新異常)が生まれる。 正規化とは、 この 「同じ事実は 1 か所にだけ」 を徹底し、 必要なときに JOIN で貼り合わせる考え方である。 SQL はこの貼り合わせと集計を 手続きではなく宣言 で書ける(「どう取るか」ではなく「何が欲しいか」を書く)点が、 for ループで組み立てる発想との決定的な違いになる。
⚠️ 落とし穴(重要・追補)── 設計と運用でつまずく 8 点
RDB は「正しく設計すれば強い」が、 裏を返せば 設計を誤ると矛盾や遅さが固定化する 。 以下は SSDSE-B-2026 を題材に、 初学者〜実務で踏みやすい落とし穴を整理したものである。
落とし穴 何が起きるか 対策
正規化不足 県名を各行にコピー → 更新異常・挿入異常・削除異常 県属性を prefectures に分離(2NF/3NF)
過剰正規化 表を細かく割りすぎ、 集計のたびに 5〜6 表 JOIN で低速・可読性低下 分析用途はマテビュー/ワイド化で非正規化を意図的に許容
NULL の扱い SUM/AVG は NULL を無視、 COUNT(*) は数える、 = NULL は常に偽IS NULL/COALESCE/AVG の分母を意識
外部キー制約の欠如 存在しない県コードが混入 → 集計が静かに狂う FOREIGN KEY を張り INSERT 時点で弾く
インデックス未設計 大規模化(SSDSE-A 市区町村版など)で全表スキャンが常態化 (pref_code, year) 複合+部分インデックス
多対多を直結 「県 ⇔ 指標」を直接つなぐと重複・不整合 中間表(facts)を挟む
トランザクション無視 複数更新の途中で失敗し、 数字が消える/二重計上 BEGIN … COMMIT で原子性(ACID)を確保
スケーラビリティ誤解 1 台で書き込みが詰まる/分散で結合が困難に 用途で分離(OLTP は行指向、 OLAP は列指向)
落とし穴の具体例:NULL と平均の罠(SSDSE-B-2026)
ある年のある県の指標が「行として存在しない(ロング形式で欠損)」場合、 AVG(value) はその欠損を 分母から自動で外す 。 これは便利だが、 「47 都道府県すべてで割った全国平均」を期待していると、 実際は「観測のあった県だけの平均」になり値が上振れする。 「NULL は 0 ではない」「NULL は欠測であって『無い=ゼロ』ではない」 を常に意識する。 全県で割りたいなら SUM(value) / 47.0 のように分母を明示するか、 CROSS JOIN で 47×年の骨組みを先に作り LEFT JOIN で埋めて欠損を可視化する(本ページ「表3」参照)。
落とし穴の具体例:多対多と中間表(架空の対応を含む)
「都道府県」と「指標」は多対多の関係にある(1 県は多数の指標を持ち、 1 指標は多数の県で観測される)。 これを 2 表だけで表そうとすると破綻する。 正解は 中間表(連関エンティティ) を挟むこと:prefectures(県マスタ)× indicators(指標マスタ)× facts(中間表=県コード・年・指標コードで一意)。 facts の主キーは (pref_code, year, indicator_code) の複合キーになり、 これが 2 本の外部キーで両マスタを参照する。 なお indicators に入れる「カテゴリ区分(人口系/家計系…)」の細分は SSDSE 本体に列が無いため、 補助的に付ける場合は 架空の分類 である旨を明記して用いること。
🚀 発展(追補)── 正規化理論から OLTP/OLAP まで
1. 正規形の階段(1NF → BCNF)
正規化は「関数従属(ある列が決まれば別の列が決まる関係)」を手がかりに冗長を段階的に排除する。 1NF :各セルは原子値(コンマ区切りで複数値を詰めない)。 2NF :1NF かつ、 非キー列が主キー全体 に従属(複合キーの一部にだけ従う部分従属を排除)。 3NF :2NF かつ、 非キー列同士の 推移的従属 を排除(例:県コード→地方区分→…を別表へ)。 BCNF :あらゆる関数従属の決定項が候補キーであること(3NF で残る稀な異常を封じる、 より厳密な形)。 教育・分析用途では 3NF を基準 に、 集計性能のため意図的に非正規化する、 という往復が現実的である。
2. インデックスと JOIN 最適化
インデックスは「本の索引」に相当し、 検索対象列に B-Tree を張ることで全表スキャン(O(n))を対数時間(O(log n))に落とす。 反面、 書き込みが遅くなり容量も食う ため万能ではない。 JOIN 最適化では、 結合キー両側にインデックスがあると ハッシュ結合/マージ結合/ネステッドループ結合 のうち最適な戦略をオプティマイザが選べる。 実務では EXPLAIN/EXPLAIN ANALYZE でクエリプランを読み、 「想定した索引が使われているか」「全表スキャンに落ちていないか」を確認するのが定石である(本ページ「実務メモ」の複合・部分インデックスも参照)。
3. ACID とトランザクション
Atomicity(原子性) :一連の操作は全て成功か全て失敗か。 Consistency(一貫性) :制約を破らない状態から状態へ遷移。 Isolation(分離性) :並行トランザクションが互いに干渉しない(分離レベルで ダーティリード ・ノンリピータブルリード ・ファントムリード のどこまで防ぐかを選ぶ)。 Durability(永続性) :コミット済みは障害後も残る。 対比として NoSQL 系の BASE (Basically Available, Soft state, Eventually consistent)は整合性を緩めて可用性・分散性を取る思想であり、 用途で選び分ける。
4. OLTP / OLAP とスタースキーマ
OLTP (オンライントランザクション処理)は小さな読み書きを高頻度で捌く用途で、 行指向ストレージ(PostgreSQL 等)が向く。 OLAP (オンライン分析処理)は大量行を集計する用途で、 列指向(DuckDB・ClickHouse 等)が桁違いに速い。 分析向けの定番設計が スタースキーマ :中央に数値の ファクト表 (SSDSE でいう facts:値・キーだけ)、 周囲に ディメンション表 (prefectures/年/indicators のような「切り口」)を放射状に配置する。 SSDSE-B-2026 の 3 表構成(県マスタ・指標マスタ・事実表)は、 そのまま小さなスタースキーマになっている。
5. RDB と NoSQL の使い分け
構造が安定し、 結合が多く、 整合性が命なら RDB 。 スキーマが流動的で、 水平分散と高スループットを優先し、 結果整合性を許せるなら NoSQL (ドキュメント/KVS/列指向ワイド/グラフ)。 近年は両者の中間として NewSQL (分散でも ACID を保つ)や、 分析特化の列指向 RDB が定着した。 SSDSE-B-2026 のような構造化統計データは、 迷わず RDB(学習は SQLite、 分析高速化は DuckDB)が第一選択である。
📎 関連ページ(このサイト内)
※ 正規化理論・ACID/トランザクション・インデックス・OLTP/OLAP・スタースキーマの各項目は、 現時点で単独ページが無いため本ページ内で解説している(テキストのみ)。