MySQLでNOT NULL制約を設定してもNULLでエラーにならない問題(無効データの制約)

【結論】

MySQLは無効または不適切なデータ値を許容しており、これらを有効な値に強制的に変更して、データの整合性を取っている

・例えば、NOT NULL制約を設定している文字列タイプのカラムにNULLを保存しようとした場合、エラーにならず、空文字("")が挿入される。

・NULL値が保存されそうになったら保存を中断したいのであれば、アプリケーション側でバリデーションを設定するか、厳密 SQL モードを有効にする必要がある

【目次】

【本題】

MySQLでNOT NULL制約を設定してもNULL値の保存でエラーにならない

MySQLでは、NOT NULL制約を設定していても、NULL値を保存する際にエラーにならない場合が存在します。

これは無効データの制約による影響です。

無効データの制約について

MySQLは無効または不適切なデータ値を許容しており、これらを有効な値に強制的に変更する。

例えば、NOT NULL制約を設定している文字列タイプのカラム(char型)にNULLを保存しようとした場合、保存は中止されず空文字("")が挿入されます。

また、数値タイプ(int型の)のであれば、数値の0が挿入されます。

もし、NULL値が保存されそうになったら保存を中断したいという意図で設定していたのであれば、その通りの挙動にはなりません。

対策1:アプリケーション側でバリデーションを設定

アプリケーション側のモデルやフロントにNULL値を許容しないバリデーションを設定しておくことで、NULL値の保存が許容されることを防ぐことが出来ます。

対策2:厳密 SQL モードを有効にする

厳密 SQL モードとは、sql_mode 値がSTRICT_TRANS_TABLES または STRICT_ALL_TABLES のいずれかあるいは両方が有効な状態を表します。

STRICT_TRANS_TABLESは、値を指定したとおりにトランザクションテーブルに挿入できない場合、ステートメントを中止します。非トランザクションテーブルの場合、値が単一行ステートメントで発生するか、複数行ステートメントの先頭行で発生した場合、ステートメントを中止します。

STRICT_ALL_TABLESは、カラムに不正な値を挿入したときに、警告ではなくエラーを返します。

SQL モードを実行時に変更するには、SET ステートメントを使用して、グローバルまたはセッションの sql_mode システム変数を設定します。

SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES';
SET SESSION sql_mode = 'STRICT_ALL_TABLES';

GLOBAL 変数を設定するには SUPER 権限が必要で、この設定はその時点以降に接続するすべてのクライアントの動作に影響します。

SESSION 変数を設定すると、現在のクライアントにのみ影響します。

なお、現在のグローバルまたはセッションの sql_mode 値を確認するには、次のステートメントを使用します。

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

参考情報

MySQL :: MySQL 8.0 リファレンスマニュアル :: 1.7.3.3 無効なデータに対する制約の施行

NOT NULLなどの制約の設定 - Ruby on Rails入門