Day7 前半のゴール
「“SQLiteで書けたCREATE TABLE”を、MySQL流に書き直せるようになる」
Day7 は、ここまで学んだ差分を一気に使う「総復習の入り口」です。
テーマはシンプルで、
SQLiteで作ったテーブル定義を
MySQLらしいテーブル定義に“設計し直す”
ことです。
ただの書き換え作業ではなく、
データ型をちゃんと選ぶ
PRIMARY KEY を見直す
FOREIGN KEY をちゃんと張る
文字コード・照合順序を意識する
必要ならインデックスも考える
という「設計のリライト」をやっていきます。
前半では、具体例を1つ決めて「どこをどう変えるか」を丁寧に分解します。
まず題材を決める
「よくある“EC風”のSQLiteテーブルをスタート地点にする」
例として、SQLiteでこんなテーブルを作っていたとします。
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT,
created_at TEXT
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
total INTEGER,
ordered_at TEXT
);
SQLありがちなポイントがいくつかあります。
PRIMARY KEY は INTEGER PRIMARY KEY(SQLite流の自動採番)
文字列は全部 TEXT
日時も TEXT
FOREIGN KEY は張っていない(user_id は“なんとなく”整数)
SQLite ではこれでも普通に動きます。
でも MySQL に持っていくなら、ここを一段“ちゃんとした設計”に変えていきます。
ステップ1:文字コードとデータベースの前提を決める
「MySQLでは“最初に土台を決める”ところから始まる」
SQLite では「ファイルを作ったらすぐCREATE TABLE」でよかったですが、
MySQL ではまず「データベース(スキーマ)」を作るところから始まります。
ここで、文字コードと照合順序を決めます。
CREATE DATABASE shop_app
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_general_ci;
SQLこれで、
このデータベース内で新しく作るテーブルは、
何も指定しなければ utf8mb4 + utf8mb4_general_ci になる
という状態になります。
Day4 でやったとおり、
utf8mb4 を選ぶことで絵文字なども含めて安全に扱える
collation を決めることで比較・ソートのルールが安定する
という「土台」が整います。
SQLiteではあまり意識しなかった部分ですが、MySQLではここが重要です。
ステップ2:users テーブルを MySQL流に書き直す
「型・AUTO_INCREMENT・NOT NULL を意識して設計する」
SQLite版の users はこうでした。
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT,
created_at TEXT
);
SQLこれを MySQL で「ちゃんと設計する」と、こうなります。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL
) ENGINE=InnoDB;
SQL1行ずつ、何を意図して変えているかを見ていきます。
id INT AUTO_INCREMENT PRIMARY KEY
SQLite の INTEGER PRIMARY KEY は、
「そのカラムがROWIDになる」という特殊な意味を持っていました。
MySQL では、
INT AUTO_INCREMENT PRIMARY KEY
と書くことで、
整数型のID
自動採番
主キー(行を一意に識別する代表)
を明示します。
ここで大事なのは、
メールアドレスなど“意味のある値”を主キーにせず、
内部用の連番IDをPRIMARY KEYにしている
という設計方針です。
これはセキュリティと運用の両方で効いてきます。
name / email を VARCHAR にする
SQLite では TEXT でしたが、MySQL では VARCHAR を使います。
name → VARCHAR(50)
email → VARCHAR(255)
「だいたいこのくらいの長さだろう」という現実を反映させています。
さらに NOT NULL を付けて、
名前もメールも必須項目
というルールをDBに持たせています。
アプリ側のバリデーションに加えて、
DB側でも「空のままは許さない」と言えるようになります。
created_at を DATETIME にする
SQLite では TEXT で日時を入れていたかもしれませんが、
MySQL では DATETIME を使います。
DATETIME にしておくと、
期間で絞り込む
日付だけ取り出す
日別集計をする
といった操作が自然に書けます。
Day2・Day3でやったように、
INSERT 時には NOW() を使うのが定番です。
ステップ3:orders テーブルを MySQL流に書き直す
「FOREIGN KEY を“ちゃんと張る”のがSQLiteとの一番の差」
SQLite版の orders はこうでした。
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
total INTEGER,
ordered_at TEXT
);
SQLMySQL版は、こう書き直します。
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
total INT NOT NULL,
ordered_at DATETIME NOT NULL,
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id)
REFERENCES users(id)
) ENGINE=InnoDB;
SQLここでのポイントは2つです。
user_id を NOT NULL + FOREIGN KEY にする
SQLite版では、user_id はただの INTEGER でした。
MySQL版では、
INT NOT NULL
FOREIGN KEY (user_id) REFERENCES users(id)
とすることで、
必ず何かしらのユーザーに紐づく
そのユーザーは users テーブルに実在していなければならない
という2つのルールをDBに持たせています。
これにより、
存在しない user_id で注文を作ろうとするとエラー
ユーザーを消そうとしたとき、注文が残っていればエラー
という「整合性のガード」が働きます。
SQLiteではアプリ側で頑張っていた部分を、
MySQL+InnoDBではDBに任せられるようになります。
ordered_at を DATETIME にする
users と同じく、日時は DATETIME にします。
これで、
特定期間の注文だけ集計する
日別売上を出す
といったクエリが書きやすくなります。
ステップ4:インデックスをどこまで考えるか
「Day6までの知識を“軽く”乗せておく」
Day7 前半では深追いしませんが、
「どこにインデックスを張る候補があるか」だけ意識しておきます。
users
PRIMARY KEY(id) に自動インデックス
email でよく検索するなら、email にインデックスを張る候補
orders
PRIMARY KEY(id) に自動インデックス
user_id で絞るクエリが多いなら、user_id にインデックス
user_id + ordered_at で複合インデックス、という選択肢もあり
この「どこが検索条件に使われるか」を考える癖が、
MySQLでのテーブル再設計ではとても大事です。
後半では、
実際にどういうクエリを想定してインデックスを張るか、
もう一歩踏み込んで考えていきます。
SQLite版とMySQL版を並べて見る
「“動くスキーマ”から“守れるスキーマ”への進化」
ここまでの変化を、ざっくり並べてみます。
SQLite版 users
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT,
created_at TEXT
);
SQLMySQL版 users
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL
) ENGINE=InnoDB;
SQLSQLite版 orders
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
total INTEGER,
ordered_at TEXT
);
SQLMySQL版 orders
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
total INT NOT NULL,
ordered_at DATETIME NOT NULL,
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id)
REFERENCES users(id)
) ENGINE=InnoDB;
SQL見た目は少し長くなりましたが、その分だけ、
型がはっきりした
必須項目が明確になった
テーブル間の関係がDBに守られるようになった
という「守りの強さ」が上がっています。
Day7 前半のまとめ
Day7 の復習課題は、「SQLiteで動いていたテーブル定義」を、そのままコピペするのではなく、MySQL流に“設計し直す”ことがテーマになる。
具体的には、データベース作成時に DEFAULT CHARACTER SET utf8mb4 と DEFAULT COLLATE を決め、各テーブルでは INT AUTO_INCREMENT PRIMARY KEY・VARCHAR・DATETIME・ENGINE=InnoDB を意識して書き直すことで、「型が曖昧なTEXTだらけのスキーマ」から卒業する。
users / orders のような親子関係のあるテーブルでは、SQLiteでただのINTEGERだった user_id を、MySQLでは NOT NULL + FOREIGN KEY (user_id) REFERENCES users(id) にすることで、「存在しないユーザーに紐づく注文」や「親が消えたのに子だけ残る」といった不整合をDBレベルで防げるようになる。
インデックスについても、PRIMARY KEYに自動で付くものに加えて、「email で検索するなら email に」「user_id で絞って ordered_at で並べるなら複合インデックス」といった形で、“どのクエリを速くしたいか”を意識した設計に一歩踏み出すことが、SQLiteからMySQLへの本当の差分になる。
後半では、
この再構築したテーブルに対して実際にINSERT / SELECTを流しながら、
FOREIGN KEYがどう効くか、インデックスをどう考えるか、
そして「自分のSQLiteプロジェクトをどうMySQLに持っていくか」の手順を、
もう一段具体的に整理していきます。

