- データベース
not-null constraintの直し方|PostgreSQL
ERROR: null value in column "email" of relation "nn_users" violates not-null constraint は、NOT NULL(空を許さない)の列に、NULL を入れようとしたときのエラーです。 SQLSTATE は 23502 です。
ERROR: null value in column "email" of relation "nn_users" violates not-null constraint
DETAIL: Failing row contains (1, a, null, 2026-10-11 03:25:11.935659+00).
エラー文の column "email" が、NULL になった列です。DETAIL の Failing row contains (...) は、入れようとした行の全体で、null の位置からも列が分かります。
| よくある原因 | 直し方 |
|---|---|
| INSERT で、その列の値を書き忘れた | 値を書く、または列に DEFAULT を付ける |
| id(serial)に NULL を明示的に送った | id を INSERT の列から外す、または DEFAULT と書く |
既定値のある列に NULL を送った(created_at など) |
列から外す。NULL を送ると既定値は使われない |
INSERT ... SELECT の元データに NULL がある |
COALESCE で置き換える、または NULL の行を先に直す |
NOT NULL を後から付けた(contains null values) |
先に NULL の行を更新してから付ける |
確認環境:PostgreSQL 16.15、psycopg 3.3.6、SQLAlchemy 2.1.4、比較に MySQL 8.0.46 を使いました(確認日 2026年10月11日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「not-null constraintの直し方|PostgreSQL」
再現に使ったテーブル
CREATE TABLE nn_users (
id serial PRIMARY KEY,
name text NOT NULL,
email text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
| INSERT | 結果 |
|---|---|
INSERT INTO nn_users (name) VALUES ('a') |
email で 23502(書き忘れ) |
INSERT INTO nn_users (id, name, email) VALUES (NULL, 'a', 'a@x') |
id で 23502 |
INSERT INTO nn_users (name, email, created_at) VALUES ('a', 'a@x', NULL) |
created_at で 23502 |
INSERT INTO nn_users (id, name, email) VALUES (DEFAULT, 'a', 'a@x') |
成功 |
INSERT INTO nn_users (name, email) VALUES ('b', 'b@x') |
成功(id・created_at に既定値) |
id に NULL を送ると、serial の番号が使われない
ERROR: null value in column "id" of relation "nn_users" violates not-null constraint
DETAIL: Failing row contains (null, a, a@x, 2026-10-11 03:25:11.938006+00).
PostgreSQL の公式ドキュメントでは、INSERT の列の一覧に書かなかった列に、既定値が入ると説明されています。列に書いて NULL を渡すと、既定値ではなく NULL を入れようとします。 created_at に NULL を渡したときも、now() は使われませんでした。
MySQL から移ってきたコードで起きやすい
INSERT INTO u VALUES (NULL, 'a')(id は自動採番の列)を、DB ごとに試しました。
| DB(id の定義) | 結果 |
|---|---|
| MySQL 8.0(AUTO_INCREMENT) | 成功(id に 1 が入った) |
| PostgreSQL 16(serial) | 23502 |
PostgreSQL 16(GENERATED BY DEFAULT AS IDENTITY) |
23502 |
PostgreSQL 16(GENERATED ALWAYS AS IDENTITY) |
cannot insert a non-DEFAULT value into column "id" |
MySQL では、自動採番の列に NULL を入れると番号が振られます。PostgreSQL では、同じ SQL がエラーになります。 id を省くか、DEFAULT と書きます。
アプリからの INSERT
| 送り方 | 結果 |
|---|---|
psycopg で {"id": None, "name": "e", ...} を全部の列に入れる SQL |
NotNullViolation(23502)、e.diag.column_name は id |
SQLAlchemy の ORM で U(id=None, name="f", ...) を add |
成功(id に番号が振られた) |
SQLAlchemy のログ(echo=True)では、送られた SQL は INSERT INTO nn_users (name, email) VALUES (...) で、id が列に含まれていませんでした。 辞書をそのまま全部の列に入れる SQL を自分で組み立てるときは、値が None の id を列から外す処理を入れます。psycopg では、エラーの列名を e.diag.column_name で取り出せました。
INSERT ... SELECT で NULL が混ざる
INSERT INTO nn_users (name, email) SELECT name, email FROM nn_src;
ERROR: null value in column "email" of relation "nn_users" violates not-null constraint
DETAIL: Failing row contains (6, d, null, 2026-10-11 03:25:11.942627+00).
1行でも NULL があると、ほかの行も含めて全体が入りません。 DETAIL の d が、問題の行の手がかりです。
SELECT * FROM nn_src WHERE email IS NULL; -- 問題の行を探す
INSERT INTO nn_users (name, email) SELECT name, COALESCE(email, '') FROM nn_src; -- 置き換えて入れる
COALESCE(email, '') で空文字にすると入りますが、「空」と「不明」の区別が消えます。 置き換える値は、業務で意味が通るものを決めます。NULL と空文字の使い分けは、既存の記事「NULL許可の設計方針|NOT NULLとデフォルト値の決め方早見表」で扱っています。
失敗した INSERT でも、番号は進む
確認環境では、23502 で3回失敗したあと、成功した INSERT の id は 4 でした。 serial の番号は、INSERT が失敗しても戻りません。id が飛ぶのは正常な動きです。
NOT NULL を後から付けるとき
ALTER TABLE nn_orders ALTER COLUMN note SET NOT NULL;
ERROR: column "note" of relation "nn_orders" contains null values
SQLSTATE は同じ 23502 ですが、メッセージが違います。 公式ドキュメントでは、SET NOT NULL は、すべての行に NULL が無いときだけ付けられ、通常はテーブル全体を確かめると説明されています。
| 手順 | SQL | 結果 |
|---|---|---|
| 1. NULL の行を直す | UPDATE nn_orders SET note = '' WHERE note IS NULL |
UPDATE 1 |
| 2. NOT NULL を付ける | ALTER TABLE nn_orders ALTER COLUMN note SET NOT NULL |
成功 |
NOT NULL の列を追加するとき
| SQL | 結果 |
|---|---|
ALTER TABLE nn_orders ADD COLUMN status text NOT NULL |
column "status" ... contains null values |
ALTER TABLE nn_orders ADD COLUMN status text NOT NULL DEFAULT 'new' |
成功(既存の行に new が入った) |
データのあるテーブルに NOT NULL の列を足すときは、DEFAULT を一緒に指定します。 公式ドキュメントでは、変わらない値(now() のように呼ぶたびに変わる値でないもの)の DEFAULT なら、テーブルの書き直しは起きないと説明されています。
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | どの列か | エラー文の column "○○" |
| 2 | どの行か | DETAIL: Failing row contains |
| 3 | アプリが NULL を送っていないか | SQL のログ・パラメータ(id・created_at) |
| 4 | 列の既定値 | information_schema.columns の column_default |
| 5 | 元データの NULL | WHERE 列 IS NULL |
| 6 | 後から付ける場合 | 先に UPDATE、追加なら DEFAULT |
よくある質問
Q. MySQL の「Column cannot be null」と同じですか。
A. 意味は同じです。MySQL 8.0 では ERROR 1048 (23000): Column 'name' cannot be null と表示されました。ただし、上で見たとおり、自動採番の列に NULL を送ったときの動きは DB で違います。
Q. 同じ番号が重複するエラーも出ます。 A. シーケンスの値と、テーブルの id がずれていることがあります。関連記事の「PostgreSQL duplicate keyの直し方|シーケンスのずれ」で扱っています。
まとめ
- NOT NULL の列に NULL を入れたエラー(23502)。
column "○○"が NULL の列 - 列に書いて NULL を送ると、既定値(serial・now())は使われない
- MySQL は自動採番の列の NULL を受け付けるが、PostgreSQL はエラー
INSERT ... SELECTは1行の NULL で全体が止まる- 後から付けるときは先に UPDATE、列を足すときは DEFAULT と一緒に
参考・出典
確認日はいずれも 2026年10月11日です。
- PostgreSQL 16 文書「INSERT」:https://www.postgresql.org/docs/16/sql-insert.html
- PostgreSQL 16 文書「ALTER TABLE」:https://www.postgresql.org/docs/16/sql-altertable.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「not-null constraintの直し方|PostgreSQL」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。