MySQL の NOT NULL と DEFAULT 制約

NOT NULL 制約と DEFAULT 値は、テーブル設計で地味ながら重要な役割を果たします。これらを適切に設定することで、アプリケーションの想定外の挙動を防ぎ、データの品質を保てます。

NOT NULL 制約とは

NOT NULL を指定したカラムには NULL 値を挿入できなくなります。INSERT 時に値を省略するとエラーになるため、「必ず値が入っている」ことをデータベースレベルで保証できます。

CREATE TABLE products (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  price DECIMAL(10,2) NOT NULL,
  description TEXT  -- NULL 許容
);

-- name を省略するとエラー
INSERT INTO products (price) VALUES (1000);
-- ERROR: Field 'name' doesn't have a default value

NULL が引き起こす問題

NULL はデータベースにおいて「値が存在しない」ことを意味しますが、プログラミング言語での null/nil とは異なる三値論理で扱われます。

比較演算の罠

NULL = NULL は TRUE ではなく NULL を返します。WHERE status = NULL ではなく WHERE status IS NULL と書く必要があり、バグの温床になりがちです。

集約関数への影響

COUNT(*) は NULL 行も数えますが、COUNT(column) は NULL を除外します。SUM や AVG も NULL 行を無視するため、意図しない計算結果になることがあります。

こうした問題を避けるために、明確な理由がない限りカラムには NOT NULL を付けるのが安全な設計方針です。

DEFAULT 値の設定

DEFAULT を指定すると、INSERT 時にそのカラムの値を省略した場合に自動で設定される値を定義できます。

CREATE TABLE articles (
  id INT AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(200) NOT NULL,
  status TINYINT NOT NULL DEFAULT 0,
  view_count INT NOT NULL DEFAULT 0,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- status と view_count は省略しても 0 が入る
INSERT INTO articles (title) VALUES ('初めての記事');

NOT NULL と DEFAULT を組み合わせることで、「NULL は入らないが、INSERT 時に毎回値を指定する必要もない」という使い勝手のよいカラムになります。

DEFAULT に使える値

使える DEFAULT 値
数値型リテラル値DEFAULT 0
文字列型リテラル値DEFAULT ''
DATETIMECURRENT_TIMESTAMPDEFAULT CURRENT_TIMESTAMP
BOOLEANTRUE / FALSEDEFAULT FALSE

MySQL 8.0.13 以降では、DEFAULT に式(Expression Default)を使えるようになりました。たとえば DEFAULT (UUID()) のような指定も可能です。ただし、すべての関数が使えるわけではないため、公式ドキュメントで確認してから使いましょう。

設計指針まとめ

カラムの要件を確認する

NULL が必要な理由があるか判断する

NOT NULL + DEFAULT で安全な設計にする

「NULL を許容する」という判断は、「このカラムには値が存在しないケースが業務上ありうる」と明確に言える場合にのみ行うべきです。たとえばユーザーの退会日(deleted_at)は、退会前は値が存在しないため NULL が自然ですが、ステータスコードのように常に何らかの値を持つべきカラムは NOT NULL DEFAULT 0 のように設計します。