ORA-02290: check constraint violated は、Oracleで INSERT や UPDATE しようとした値が、テーブルに定義された CHECK 制約の条件を満たさない時に発生するエラーです。
たとえば、ステータス列は 'ACTIVE' または 'INACTIVE' のみ、金額は0以上、区分は決められた値のみ、というルールがCHECK制約で定義されている場合、その条件から外れた値を入れるとORA-02290になります。
エラーに表示された制約名を使って
USER_CONSTRAINTS / ALL_CONSTRAINTS を検索し、SEARCH_CONDITION の条件を確認します。そのうえで、投入値を直す、制約定義を見直す、既存データを修正する、の順に判断します。ORA-02290とは
Oracle公式のエラー説明では、ORA-02290は挿入しようとした値が名前付きCHECK制約を満たさない場合に発生します。対処は、制約に違反する値を登録しないことです。
制約全体の基本は 制約(CONSTRAINT)完全ガイド、INSERT/UPDATEの基本は INSERT・UPDATE・DELETE完全ガイド も参考になります。
まず制約名を確認する
ORA-02290のメッセージには、通常 (スキーマ名.制約名) が表示されます。この制約名から、どのテーブルのどんな条件に違反したのかを調べます。
ORA-02290: check constraint (APP.CK_ORDER_STATUS) violated
SELECT owner,
table_name,
constraint_name,
constraint_type,
status,
validated,
search_condition
FROM all_constraints
WHERE owner = UPPER('&OWNER')
AND constraint_name = UPPER('&CONSTRAINT_NAME');
SEARCH_CONDITIONが見づらい場合
環境やクライアントによっては、SEARCH_CONDITION が長くて見づらい、または LONG 型の扱いで取得しづらいことがあります。SQL*PlusやSQLclでは SET LONG を大きめにしてから確認します。
SET LONG 100000
SET PAGESIZE 100
SET LINESIZE 200
SELECT constraint_name,
search_condition
FROM user_constraints
WHERE constraint_name = UPPER('&CONSTRAINT_NAME');
新しい環境では SEARCH_CONDITION_VC が使える場合もあります。短い条件であればこちらの方が扱いやすいです。
SELECT constraint_name,
search_condition_vc
FROM user_constraints
WHERE constraint_name = UPPER('&CONSTRAINT_NAME');
制約列を確認する
CHECK制約は複数列を使っている場合があります。制約名だけでなく、どの列が関係しているかも確認します。
SELECT c.owner,
c.table_name,
c.constraint_name,
cc.column_name,
cc.position,
c.search_condition
FROM all_constraints c
LEFT JOIN all_cons_columns cc
ON cc.owner = c.owner
AND cc.constraint_name = c.constraint_name
WHERE c.owner = UPPER('&OWNER')
AND c.constraint_name = UPPER('&CONSTRAINT_NAME')
ORDER BY cc.position;
constraint_type が C の場合、CHECK制約またはNOT NULL制約系の制約です。NOT NULL違反は通常 ORA-01400 で見ることが多いため、必要に応じて ORA-01400 完全ガイド も確認してください。
最小の再現例
次のテーブルでは、status は ACTIVE または INACTIVE のみ許可されます。
CREATE TABLE app_user_status (
user_id NUMBER PRIMARY KEY,
status VARCHAR2(20),
CONSTRAINT ck_user_status
CHECK (status IN ('ACTIVE', 'INACTIVE'))
);
INSERT INTO app_user_status (user_id, status) VALUES (1, 'STOPPED'); -- ORA-02290: check constraint (APP.CK_USER_STATUS) violated
INSERT INTO app_user_status (user_id, status) VALUES (1, 'ACTIVE');
テーブル作成時の制約定義は CREATE TABLE完全ガイド も参考になります。
発生しやすいパターン
区分値が定義外
status IN ('A','I') のような条件に対して、画面やCSVから未定義のコードが入るケースです。
数値の範囲外
amount >= 0、rate BETWEEN 0 AND 100 などの条件に違反するケースです。
日付の範囲外
開始日と終了日の関係、未来日禁止、基準日以降のみ許可などの条件に違反するケースです。
複数列の組み合わせ違反
end_date IS NULL OR end_date >= start_date のように、単一列ではなく行全体の整合性を見ているケースです。
NLSや文字大小の違い
大文字のみ許可しているのに小文字が来る、日付文字列の解釈が環境で違う、などのケースです。
違反データを探すSQL
制約条件が分かったら、その条件を反転して違反データを探します。既存データ修正や、制約を有効化できない原因調査にも使えます。
SELECT user_id,
status
FROM app_user_status
WHERE status NOT IN ('ACTIVE', 'INACTIVE')
OR status IS NULL;
SELECT order_id,
amount,
discount_rate
FROM orders
WHERE amount < 0
OR discount_rate < 0
OR discount_rate > 100;
CHECK制約は
NULL を含むと三値論理の影響を受けます。単純に NOT (条件) と書くだけでは、NULLを含む行の扱いが意図とずれることがあります。NULLはCHECK制約に通ることがある
OracleのCHECK制約は、条件が FALSE になった場合に違反します。条件が TRUE または UNKNOWN の場合は違反になりません。そのため、列がNULLの時はCHECK制約を通ることがあります。
CREATE TABLE check_null_sample ( amount NUMBER, CONSTRAINT ck_amount_positive CHECK (amount > 0) ); -- amount > 0 は UNKNOWN になるため、CHECK制約だけなら通る INSERT INTO check_null_sample (amount) VALUES (NULL); -- NULLも禁止したい場合 ALTER TABLE check_null_sample MODIFY amount NOT NULL;
NULLを禁止したい場合は、CHECK制約だけに頼らず NOT NULL 制約も定義します。
制約を一時的に無効化してよいか
移行作業や一括修正のためにCHECK制約を一時的に無効化することはできます。ただし、無効化中に不正データが入ると、再有効化時に失敗します。本番では、無効化前後のデータ検証手順を必ず用意します。
-- 一時的に無効化 ALTER TABLE app_user_status DISABLE CONSTRAINT ck_user_status; -- データ修正後、既存データも検証して有効化 ALTER TABLE app_user_status ENABLE VALIDATE CONSTRAINT ck_user_status; -- 既存データは検証せず、以後のDMLだけ制約する場合 ALTER TABLE app_user_status ENABLE NOVALIDATE CONSTRAINT ck_user_status;
ENABLE VALIDATE と ENABLE NOVALIDATE の違いは、既存データを検証するかどうかです。制約の有効化/無効化は便利ですが、データ品質に直結するため運用ルールを決めて使います。
大量取込ではLOG ERRORSで違反行を分ける
CSV取込や大量INSERTで一部の行だけORA-02290になる場合、処理全体を止めるより、DBMS_ERRLOG と LOG ERRORS でエラー行を別表へ退避する方法があります。後から違反行だけを確認・修正できるため、データ移行や一括取込で有効です。
BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG(dml_table_name => 'APP_USER_STATUS');
END;
/
INSERT INTO app_user_status (user_id, status)
SELECT user_id, status
FROM staging_user_status
LOG ERRORS INTO err$_app_user_status ('ORA-02290') REJECT LIMIT UNLIMITED;
DBMS_ERRLOG の詳しい使い方は エラーログを自動収集するDBMS_ERRLOGの使い方 を参照してください。
制約名がSYS_C…で分かりにくい場合
制約名を明示せずに作成すると、SYS_C0012345 のような自動名になります。この場合でも ALL_CONSTRAINTS から条件は確認できますが、運用時には意味のある名前を付ける方が調査しやすくなります。
ALTER TABLE app_user_status RENAME CONSTRAINT sys_c0012345 TO ck_user_status;
関連エラーとの違い
ORA-02290
CHECK制約の条件に違反しています。制約名から SEARCH_CONDITION を確認します。
ORA-02291
外部キー制約で、参照先の親キーが存在しないエラーです。詳細は ORA-02291の原因と解決方法 を参照してください。
ORA-02292
親レコードを削除しようとしたが、子レコードが存在するエラーです。詳細は ORA-02292の原因と解決方法 を参照してください。
ORA-01438
数値列の精度や桁数を超えたエラーです。CHECK制約ではなく列定義そのものを確認します。詳細は ORA-01438の原因と解決方法 を参照してください。
ORA-00001
UNIQUE制約や主キー制約に違反しています。重複データを確認します。詳細は ORA-00001の原因と解決方法 を参照してください。
修正の流れ
- ORA-02290のメッセージから制約名を確認する
ALL_CONSTRAINTSまたはUSER_CONSTRAINTSでSEARCH_CONDITIONを確認するALL_CONS_COLUMNSで関係する列を確認する- INSERT/UPDATEしようとしている値が条件を満たすか確認する
- 既存データが原因なら、条件を反転したSQLで違反行を洗い出す
- 仕様変更なら、データを直すのか制約定義を変えるのかを決める
- 制約を無効化する場合は、再有効化時の検証手順を用意する
よくある質問
どの列が原因かエラーだけで分かりますか?
ORA-02290のエラーには制約名が表示されます。その制約名を ALL_CONSTRAINTS と ALL_CONS_COLUMNS で調べると、条件と関連列を確認できます。
NULLを入れたのにCHECK制約で止まりません
CHECK制約は条件がFALSEの時に違反します。NULLによりUNKNOWNになる条件は通ることがあります。NULLも禁止したい場合は NOT NULL を併用します。
制約を無効化して取り込んでもよいですか?
移行作業では選択肢になりますが、不正データが入るリスクがあります。取り込み後に違反データを修正し、ENABLE VALIDATE で再検証する運用にします。
まとめ
ORA-02290は、INSERTやUPDATEの値がCHECK制約の条件を満たさない時に発生します。まずエラーに表示された制約名を使い、SEARCH_CONDITION と関係列を確認します。
値を直すだけで解決する場合もありますが、仕様変更、既存データ、NULLの扱い、NLS、制約定義そのものが原因のこともあります。制約を無効化する場合は、必ず再有効化とデータ検証まで含めて対応してください。

