MySQL ERROR 1364: Field doesn't have a default value を INSERT 時に修正する

beginner🗄️ MySQL2026-05-31| MySQL 5.7+、MySQL 8.0 — Linux/macOS/Windows、任意のクライアント(CLI、PHP、Python、Node.js)

Error Message

ERROR 1364 (HY000): Field 'column_name' doesn't have a default value
#mysql#sqlエラー#insert#厳格モード

状況の説明

以前の開発マシン、または古いMySQLバージョンでは問題なく動いていたINSERT文が、突然エラーになりました:

ERROR 1364 (HY000): Field 'created_by' doesn't have a default value

クエリは何も変えていません。カラムも存在しています。それでもMySQLはINSERTを拒否します。原因はStrictモード(厳格モード)です。サーバーのアップグレードや環境移行後によく発生します。

なぜこうなるのか

MySQL 5.7以降、STRICT_TRANS_TABLESがデフォルトのsql_modeに含まれるようになりました。それ以前は、デフォルト値のないNOT NULLカラムを省略しても、空文字列やゼロが黙って格納されていました。Strictモードが有効な場合、MySQLはINSERTを拒否します。

エラーが発生するカラムは通常、以下の条件を満たしています:

  • NOT NULLとして定義されている
  • DEFAULT値が設定されていない
  • INSERT文に含まれていない

カラムの定義を確認してみましょう:

SHOW CREATE TABLE your_table\G

以下のような定義が見つかれば、それが原因です:

`created_by` varchar(100) NOT NULL,

— DEFAULTなし、かつNOT NULL — これが問題の原因です。

手っ取り早い修正:INSERT文にカラムを含める

最も早い修正方法は、不足している値を指定することです。

-- 修正前(失敗する)
INSERT INTO orders (product_id, quantity) VALUES (42, 3);

-- 修正後(成功する)
INSERT INTO orders (product_id, quantity, created_by) VALUES (42, 3, 'system');

そのカラムに常に適切なデフォルト値があるべきなら、スキーマ側で設定しておくことで、クエリを書くたびに意識する必要がなくなります。

恒久的な修正:カラムにDEFAULTを追加する

カラムに常に適切なフォールバック値があるべき場合は、スキーマにそのデフォルトを組み込んでしまいましょう:

ALTER TABLE orders
  MODIFY COLUMN created_by VARCHAR(100) NOT NULL DEFAULT 'system';

タイムスタンプ系のカラムではよくこの対応が行われます:

ALTER TABLE orders
  MODIFY COLUMN created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;

ALTER TABLE orders
  MODIFY COLUMN updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

ALTER実行後は、元のINSERT文をクエリコードを一切変更せずに実行できるようになります。

代替案:カラムをNULL許容にする

「値なし」がそのカラムとして完全に有効な状態である場合もあります。NOT NULL制約を外しましょう:

ALTER TABLE orders
  MODIFY COLUMN created_by VARCHAR(100) DEFAULT NULL;

INSERT文から省略すると、NULLが黙って格納されます。Strictモードはこれを許容します。

緊急の回避策:セッション単位でStrictモードを無効化する

マイグレーションの途中や、テーブルを変更できない状況で、今すぐINSERTを通す必要がある場合があります。現在のセッションのみStrictモードを無効化できます。他の接続には影響しません:

SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''));

-- 失敗していたINSERTを実行
INSERT INTO orders (product_id, quantity) VALUES (42, 3);

-- Strictモードを元に戻す(または接続を閉じる)
SET SESSION sql_mode = @@GLOBAL.sql_mode;

**これを恒久的な修正として扱わないでください。**Strictモードはデータの問題をデータベースに入る前に検出するために存在します。グローバルに無効化すると、本来空であってはいけないフィールドに空文字列やゼロが黙って格納されるようになります。

グローバルsql_modeの確認と変更

現在有効な設定を確認するには:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

すべての接続に対して変更を恒久的に適用するには、MySQLの設定ファイルを編集します。ディストリビューションによって通常は/etc/mysql/mysql.conf.d/mysqld.cnfまたは/etc/my.cnfです:

[mysqld]
sql_mode = "ONLY_FULL_GROUP_BY,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"

その後、再起動します:

sudo systemctl restart mysql

STRICT_TRANS_TABLESを外すことはトレードオフです。データの安全性を互換性と引き換えにすることになります。「黙って格納される」INSERTが実際にどんな値をテーブルに書き込むかを監査してから、この方法を選択してください。

アプリケーション側:INSERT前にデフォルト値をセットする

このエラーが発生しているアプリケーションコードでは、クエリを回避するのではなく、クエリを実行する前に値をセットする必要があります。PythonとSQLAlchemyの場合:

from datetime import datetime

new_order = Order(
    product_id=42,
    quantity=3,
    created_by=current_user.username or 'system',
    created_at=datetime.utcnow()
)
db.session.add(new_order)
db.session.commit()

PHP PDOの場合:

$stmt = $pdo->prepare(
    "INSERT INTO orders (product_id, quantity, created_by) VALUES (?, ?, ?)"
);
$stmt->execute([42, 3, $user ?? 'system']);

修正の確認

以下を実行して、すべて正しく反映されているか確認しましょう:

-- 元の失敗していたINSERTを再実行
INSERT INTO orders (product_id, quantity) VALUES (42, 3);

-- 結果: Query OK, 1 row affected となるはずです

-- レコードを確認
SELECT * FROM orders ORDER BY id DESC LIMIT 1;

カラムを変更した場合は、新しいデフォルト値がスキーマに反映されているか確認してください:

SHOW COLUMNS FROM orders LIKE 'created_by';
-- Field      | Type         | Null | Key | Default | Extra
-- created_by | varchar(100) | NO   |     | system  |

根本原因のまとめ

  • MySQL 5.7以降でStrictモードがデフォルトで有効になった — これが最も一般的な原因
  • DEFAULTなしでNOT NULLと宣言されたカラムは、すべてのINSERT文に含める必要がある
  • 最善の修正:カラムにDEFAULTを追加するか、クエリに明示的に含める
  • セッション単位のsql_modeオーバーライドは最後の手段です — 恒久的な解決策ではありません

Related Error Notes