MySQL の created_at / updated_at 設計

ほとんどのアプリケーションで「レコードがいつ作られたか」「いつ更新されたか」を記録する必要があります。created_at と updated_at はそのための定番カラムで、テーブル設計のテンプレートとも言える存在です。

基本的な定義

CREATE TABLE articles (
  id INT AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(200) NOT NULL,
  body TEXT NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

created_at は INSERT 時に自動で現在日時が入り、updated_at は INSERT 時と UPDATE 時の両方で自動更新されます。アプリケーション側で値を指定する必要がなく、MySQL が自動的に管理してくれます。

DATETIME と TIMESTAMP どちらを使うか

タイムスタンプカラムの型としては DATETIME と TIMESTAMP の2択になりますが、それぞれ特徴があります。

DATETIME を使う場合

2038年問題の影響を受けない。タイムゾーン変換が行われないため、アプリケーション側でタイムゾーンを統一管理する設計に向いている。格納範囲が広い。

TIMESTAMP を使う場合

内部的に UTC で格納されるため、グローバル環境でセッションのタイムゾーンに応じた表示が自動で行われる。ストレージが1バイト少ない。ただし 2038年1月19日が上限。

長期運用を考えると DATETIME を選ぶのが安全です。最近のプロジェクトでは DATETIME を採用するケースが増えています。

ON UPDATE CURRENT_TIMESTAMP の挙動

ON UPDATE CURRENT_TIMESTAMP は、行のいずれかのカラムが変更された場合に自動で現在日時に更新される仕組みです。ただし、UPDATE 文を実行しても値が実際に変わらなかった場合は updated_at も更新されません。

-- title が実際に変わるので updated_at も更新される
UPDATE articles SET title = '新しいタイトル' WHERE id = 1;

-- title が同じ値なので updated_at は変わらない
UPDATE articles SET title = title WHERE id = 1;

この挙動を知らないと、「UPDATE したのに updated_at が変わらない」と混乱することがあります。

小数秒の精度

ミリ秒やマイクロ秒の精度が必要な場合は、DATETIME(3) や DATETIME(6) を使います。

CREATE TABLE event_logs (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  event_type VARCHAR(50) NOT NULL,
  created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
);

ログやイベント追跡では同一秒内に複数のレコードが生成されることがあるため、ソート順を正確に保つために小数秒の精度が役立ちます。

タイムゾーンの統一

UTC で統一する方式

すべての日時を UTC で格納し、表示時にユーザーのタイムゾーンに変換します。サーバーの所在地に依存しない一貫した設計になります。グローバルなサービスではこの方式が標準的です。

ローカルタイムで格納する方式

日本国内のみで運用するサービスであれば、JST で格納するのも実用的な選択です。ただし、将来の海外展開や夏時間のある地域への対応が困難になるリスクがあります。

アプリケーションフレームワークとの連携

Laravel の Eloquent や Rails の ActiveRecord のような ORM は、created_at / updated_at を自動管理する機能を標準で備えています。フレームワークの規約に合わせたカラム名にしておくと、追加の設定なしでタイムスタンプが自動管理されます。

フレームワークデフォルトのカラム名
Laravelcreated_at / updated_atTIMESTAMP
Railscreated_at / updated_atDATETIME
Djangoauto_now_add / auto_nowDATETIME

フレームワークのデフォルトに従うのが最も楽ですが、プロジェクトの方針として DATETIME 型に統一するなどの調整が必要な場合もあります。いずれにせよ、created_at と updated_at はほぼすべてのテーブルに設定しておくべき基本カラムです。