PostgreSQLの「null value in column violates not-null constraint」エラーを修正する

beginner🐘 PostgreSQL2026-03-22| PostgreSQL 12以降、Linux/macOS/Windows、任意のクライアント(psql、pgAdmin、アプリケーションコード)

Error Message

ERROR: null value in column "user_id" of relation "orders" violates not-null constraint DETAIL: Failing row contains (null, 101, 2024-01-15).
#postgresql#constraint#null#insert#schema

エラーの内容

INSERT または UPDATE を実行すると、PostgreSQL が即座にエラーを返します:

ERROR: null value in column "user_id" of relation "orders" violates not-null constraint
DETAIL: Failing row contains (null, 101, 2024-01-15).

メッセージは簡潔ですが有用です。user_idNULL を書き込もうとしましたが、そのカラムには NOT NULL 制約があります。つまり、いかなる例外もなく、常に実際の値が必要です。

なぜこのエラーが発生するのか

いくつかのシナリオでこのエラーが発生します。最もよくあるケースは次のとおりです:

  • INSERT 文でカラムを完全に省略しており、DEFAULT も定義されていない — PostgreSQL には代わりに使える値がありません。
  • アプリケーションコードが事前にバリデーションを行わず、None / null / undefined をそのままクエリに渡している。
  • 誰かが既存のカラムに NOT NULL 制約を追加したが、古いアプリコードはそれを知らず、そのフィールドをスキップし続けている。
  • ORM モデルやマイグレーションスクリプトが実際のテーブルスキーマと乖離している。

ステップ 1 — テーブルスキーマを確認する

何かを変更する前に、どのカラムが実際に NOT NULL を強制しているか確認しましょう。psql で実行:

\d orders

または、pgAdmin や DBeaver などあらゆるクライアントで使えるこのクエリを利用する:

SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'orders'
ORDER BY ordinal_position;

is_nullable = 'NO' かつ column_default が空の行に注目してください。そのカラムはすべての INSERT で明示的な値が必要であり、デフォルト値による救済はありません。

ステップ 2 — ステートメントのバグを特定する

INSERT 文とスキーマの出力を並べて比較しましょう。このエラーの大半は 2 つのパターンから発生します。

1 つ目:カラムを完全に省略しているケース:

-- user_id が抜けている — PostgreSQL は NULL を受け取る
INSERT INTO orders (product_id, order_date)
VALUES (101, '2024-01-15');

2 つ目:明示的に NULL を渡しているケース:

INSERT INTO orders (user_id, product_id, order_date)
VALUES (NULL, 101, '2024-01-15');  -- 即座に拒否される

いずれの場合も、PostgreSQL は NULL を受け付けないカラムに NULL が書き込まれようとしているのを検知します。

ステップ 3 — 修正方法を選択する

オプション A:不足している値を指定する(約 80% のケースに対応)

必要なカラムに実際の値を含めるだけです:

INSERT INTO orders (user_id, product_id, order_date)
VALUES (42, 101, '2024-01-15');

アプリケーションコードから INSERT する場合は、クエリ実行後ではなく、実行前にバリデーションを行いましょう:

# Python の例
if user_id is None:
    raise ValueError("user_id is required before inserting an order")

cursor.execute(
    "INSERT INTO orders (user_id, product_id, order_date) VALUES (%s, %s, %s)",
    (user_id, product_id, order_date)
)

オプション B:カラムに DEFAULT を追加する

合理的なデフォルト値がある場合はどうでしょうか?たとえば、自動システムによる注文は常にユーザー 0 に紐付けるケースです。カラムに一度だけ設定します:

ALTER TABLE orders
  ALTER COLUMN user_id SET DEFAULT 0;

これにより、user_id を省略した INSERT はエラーを出さずに 0 を使用するようになります。

オプション C:NOT NULL 制約を削除する(NULL が本当に有効な場合のみ)

ゲストチェックアウトはその好例です。注文にユーザーが紐付かないことが正当にありえます。その場合は:

ALTER TABLE orders
  ALTER COLUMN user_id DROP NOT NULL;

エラーを黙らせるためだけにこれを行わないでください。NULL がデータモデルにおいて実際の意味を持つ場合にのみ実施しましょう。

オプション D:NOT NULL を適用する前に既存の行をバックフィルする

既にデータが存在するテーブルに新しい NOT NULL カラムを追加する場合、既存の行に NULL が入っているためマイグレーションがブロックされます。先にバックフィル、次に制約の適用という順序で行います:

-- ステップ 1:カラムを追加し、いったん NULL を許可する
ALTER TABLE orders ADD COLUMN user_id INTEGER;

-- ステップ 2:既存の全行に実際の値を設定する
UPDATE orders SET user_id = 0 WHERE user_id IS NULL;

-- ステップ 3:制約を適用する
ALTER TABLE orders ALTER COLUMN user_id SET NOT NULL;

ステップ 2 をスキップすることが、このエラーに関するマイグレーションで最もよく見られるミスです。

ステップ 4 — 修正を確認する

RETURNING * を付けて元のステートメントを再実行します:

INSERT INTO orders (user_id, product_id, order_date)
VALUES (42, 101, '2024-01-15')
RETURNING *;

成功すると PostgreSQL が挿入した行を返します。エラーが出なければ制約は満たされています。データが正しく保存されたか確認しましょう:

SELECT * FROM orders WHERE product_id = 101 ORDER BY order_date DESC LIMIT 5;

今後の予防策

  • クエリの中ではなく、実行前にバリデーションを行うNone/null はアプリケーション層で排除しましょう。値がデータベースに到達する時点では、すでにクリーンな状態であるべきです。
  • INSERT では常に RETURNING を使用する — 書き込みの成功を確認し、保存された内容を正確に把握できます。低コストの保険です。
  • 制約を適用する前にバックフィルする — デフォルト値なしで稼働中のテーブルに NOT NULL を追加すると、既存の行に NULL がある場合は即座に失敗します。上記の 3 ステップのマイグレーションが安全なパターンです。
  • ORM モデルを同期させ続ける — マイグレーションのたびに、SQLAlchemy・Django ORM・Prisma のモデルが実際のスキーマを反映しているか確認しましょう。モデルとテーブルの乖離はサイレントなバグの温床です。
  • エラーメッセージをよく読む — PostgreSQL はエラー内にカラム名("user_id")とテーブル名("orders")を明示しています。スキーマ全体を眺めるのではなく、そのカラムに直接アクセスしましょう。

Related Error Notes