列制約 — NOT NULL / UNIQUE / CHECK / 主キー この記事は、基礎から複雑なSQL,SQLチューニングまでSQLの実践的なスキルを1からマスターする「SQL入門講座の一部」です。 PRIMARY KEY/NOT NULL/UNIQUE/CHECKの宣言方法をapp_memberなどのテーブルで設計し、わざと制約違反のINSERTを実行してconstraint failedエラーが不正データを弾く挙動を体験します。
列制約とは — 不正なデータを DB 側で防ぐ 列制約 (column constraint、列に設定する入力ルール)は、CREATE TABLEの列定義に書く「この列に入れてよい値の条件」です。
本記事では代表的な 4 つ — PRIMARY KEY (主キー。
行を一意に識別)、NOT NULL (NULL を禁止)、UNIQUE (重複を禁止)、CHECK (条件式を満たさない値を禁止)— を扱います。
これまでの記事と違い、データは用意せず、演習で自分でテーブルを作り、わざと制約に違反するINSERTを実行してエラーを体験 します。
4 つの列制約の役割 PRIMARY KEY 行を一意に識別 NULL も重複も不可 NOT NULL NULL を禁止 値の入力を必須化 UNIQUE 重複した値を禁止 (NULL は許容) CHECK 条件式が真でない 値を禁止 各制約は「列に入れてよい値」を制限します。違反する INSERT / UPDATE は DB がエラーで弾き、不正なデータがテーブルに入るのを防ぎます。 制約付きのテーブルを宣言する 制約は列定義の型のうしろに続けて書きます。
列名 型 制約 制約 ...の形で、1 列に複数の制約を並べられます。
PRIMARY KEYをINTEGER列に付けると、その列が行を一意に識別する主キーになります。
NOT NULLは値の省略を禁止、UNIQUEは他の行と同じ値を禁止、CHECK (条件式)は条件式が真にならない値を禁止します。
下のmemberテーブルは、member_idを主キー、handle(ユーザー名)を NOT NULL かつ UNIQUE、ageに「0 以上」の CHECK を付けた定義です。
-- 制約付きテーブルの定義例(読むだけ)
CREATE TABLE IF NOT EXISTS member (
member_id INTEGER PRIMARY KEY , -- 主キー: 一意かつ NULL 不可
handle TEXT NOT NULL UNIQUE , -- 必須かつ重複禁止
email TEXT UNIQUE , -- 重複禁止(NULL は許容)
age INTEGER CHECK (age >= 0 ) -- 0 以上のみ許可
);
-- 制約をすべて満たす行は問題なく入る
INSERT INTO member (member_id, handle, email, age)
VALUES ( 1 , 'alice' , 'alice@example.com' , 30 );
「会員テーブルを、不正データが入らないよう制約付きで設計したい」という要件を想定します。(正しく実行できれば解説が表示されます)
① まずDROP TABLE IF EXISTS app_member;で前回分を消してから、app_memberテーブルを作ってください。列はmember_id(整数・主キー)、handle(テキスト・必須かつ重複禁止)、email(テキスト・重複禁止だが NULL は許容)、age(整数・0 以上のみ)です。
② すべての制約を満たす行を 1 件INSERTしてください(例: member_id 1 / handle ascii の文字列 / email ascii の文字列 / age は 0 以上の整数)。
③ SELECT * FROM app_member;で 1 行入っていることを確認してください。
制約違反はエラーになる — NOT NULL と UNIQUE 制約の役割は「違反した書き込みを拒否する」ことです。
NOT NULL列に NULL(または値を省略)を入れようとするとNOT NULL constraint failedエラー、UNIQUE列・PRIMARY KEY列に既存と同じ値を入れようとするとUNIQUE constraint failedエラーになります。
エラーになったINSERTの行はテーブルに入りません。
制約違反 INSERT の流れ 制約を満たす INSERT DB が受理 行が追加される 制約に違反する INSERT constraint failed エラー 行は追加されない (不正データを防ぐ) 制約を満たす行は通常どおり入りますが、違反する行は DB がエラーで拒否し、テーブルには追加されません。エラーは「制約が機能している」証拠です。 -- NOT NULL 違反: handle に NULL を入れようとする(読むだけ)
INSERT INTO member (member_id, handle, age) VALUES ( 2 , NULL , 20 );
--> Error: NOT NULL constraint failed: member.handle
-- UNIQUE 違反: 既存と同じ handle を入れようとする(読むだけ)
INSERT INTO member (member_id, handle, age) VALUES ( 3 , 'alice' , 25 );
--> Error: UNIQUE constraint failed: member.handle
「handleを必須にした効果を、違反 INSERT で確かめたい」という要件を想定します。この演習はエラーが出れば成功 です(制約が正しく不正データを弾いている証拠になります)。
① 冒頭のDELETE FROM app_member;で、実践 1 で作ったapp_memberを空に戻してください(再実行しても同じ結果になるようにするためです)。
② NOT NULL のhandle列にわざとNULLを入れるINSERTを自分で書いてください(例: member_id 1 / handle NULL / email NULL / age 20)。
③ 実行して、NOT NULL constraint failed: app_member.handleのエラーが出ることを確認してください。エラーメッセージの末尾に、どのテーブルのどの列で違反したかが示される点も読んでみてください。
「handleを重複禁止にした効果を、違反 INSERT で確かめたい」という要件を想定します。この演習もエラーが出れば成功 です。
① 冒頭のDELETE FROM app_member;で、実践 1 で作ったapp_memberを空に戻してください。
② 1 行目として、handleが'alice'の行を自分でINSERTしてください(例: member_id 1 / handle 'alice' / email 'alice@example.com' / age 30)。
③ 2 行目として、handleが同じく'alice'の別の行を自分でINSERTしてください(member_id や email は変えて構いません)。
④ 実行して、1 件目は正常に入り、2 件目でUNIQUE constraint failed: app_member.handleのエラーが出ることを確認してください。
CHECK 制約 — 値の条件を式で表す CHECK (条件式)は、列に入れてよい値を式で表現する制約です。
CHECK (age >= 0)なら負の年齢を、CHECK (price > 0)なら 0 以下の価格を拒否できます。
CHECK (status IN ('active','inactive'))のように、許可する値の集合を限定する使い方も頻出です。
条件式が真にならない値をINSERT / UPDATEしようとするとCHECK constraint failedエラーになります。
本記事の最後の演習として、CHECK (age >= 0)とCHECK (status IN (...))を題材に、CHECK 制約が違反を弾く挙動を体験します。
CHECK 制約の真偽判定 INSERT する値 条件式の評価 結果 age = 30 age >= 0 → 真 受理 行が追加 age = -5 age >= 0 → 偽 CHECK constraint failed status = 'banned' status IN ('active','inactive') → 偽 CHECK constraint failed INSERT する各行について CHECK の条件式を評価します。真なら受理、真でなければ CHECK constraint failed として拒否されます。 -- CHECK で値の範囲・集合を制限する(読むだけ)
CREATE TABLE IF NOT EXISTS product (
product_id INTEGER PRIMARY KEY ,
price INTEGER CHECK (price > 0 ),
status TEXT CHECK ( status IN ( 'active' , 'inactive' ))
);
-- price <= 0 は CHECK constraint failed で拒否される
INSERT INTO product (product_id, price, status ) VALUES ( 1 , - 100 , 'active' );
「ageに 0 以上の CHECK を付けた効果を、違反 INSERT で確かめたい」という要件を想定します。エラーが出れば成功 です。
① 冒頭のDELETE FROM app_member;で、実践 1 で作ったapp_memberを空に戻してください。
② ageにわざと負の値(例: -5)を入れるINSERTを自分で書いてください。handleは NOT NULL なので NULL 以外の値を入れます。
③ 実行して、CHECK constraint failed: app_member.ageのエラーが出ることを確認してください。ageを 0 以上の値に書き換えれば INSERT が通ることも、余裕があれば試してみてください。
「会員の状態列statusを'active' / 'inactive'の 2 値だけに限定したい」という要件を想定します。本記事の最後の演習で、エラーが出れば成功 です。
① まずDROP TABLE IF EXISTS app_member_status;で前回分を消してから、app_member_statusテーブルを自分で作ってください。列はmember_id(整数・主キー)とstatus(テキスト・必須かつ'active'または'inactive'のみ許可)の 2 列です。
② 1 行目として、許可値の'active'(または'inactive')をINSERTしてください。
③ 2 行目として、許可リストに無い'banned'をINSERTしてください。
④ 実行して、1 件目は通り、2 件目でCHECK constraint failed: app_member_status.statusのエラーが出ることを確認してください。
Q1 handle TEXT NOT NULL UNIQUEと宣言された列について、正しい説明はどれですか。
NULL は入れられるが、重複する値は入れられない 値の省略・NULL も、他の行と重複する値も、どちらも入れられない 重複は許されるが NULL は入れられない 数値しか入れられない
Q2 UNIQUE制約のある列に、すでに存在する値と同じ値をINSERTしようとするとどうなりますか。
古い行が新しい行で上書きされる UNIQUE constraint failedエラーになり、その行は追加されない重複した値が NULL に変換されて入る 警告は出るが行は追加される
Q3 「価格は 0 より大きい値だけを許可する」という業務ルールをテーブル定義で表現するのに最も適した制約はどれですか。
price INTEGER NOT NULLprice INTEGER UNIQUEprice INTEGER CHECK (price > 0)price INTEGER PRIMARY KEY
前へUPSERT(ON CONFLICT)と一括 INSERT 応用 次へ 外部キー制約と参照アクション(ON DELETE / ON UPDATE)