0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

本記事では、SQLiteのテーブル制約の種類と使い方を
備忘録としてまとめます。

制約はデータの整合性をDBレベルで保証するための仕組みです。
アプリ側のチェック処理を減らせるので積極的に活用しています。


1. 制約の種類一覧

制約 キーワード 内容
主キー制約 PRIMARY KEY 行を一意に識別する
NOT NULL制約 NOT NULL NULL値を禁止する
UNIQUE制約 UNIQUE 重複値を禁止する
CHECK制約 CHECK 条件を満たす値のみ許可する
DEFAULT制約 DEFAULT 値が未指定のときのデフォルト値
外部キー制約 FOREIGN KEY 他テーブルとの参照整合性を保つ

2. PRIMARY KEY(主キー制約)

行を一意に識別するための制約です。
NULLは禁止・重複も禁止になります。

-- 列レベルで設定
CREATE TABLE users (
    id   INTEGER PRIMARY KEY, -- 自動採番(ROWIDの別名)
    name TEXT
);

-- テーブルレベルで設定(複合主キー)
CREATE TABLE order_parts (
    order_id INTEGER,
    part_id  INTEGER,
    PRIMARY KEY (order_id, part_id) -- 2列の組み合わせで一意
);

INTEGER PRIMARY KEYはAUTOINCREMENTと同等の動作をします。
値を省略すると自動的に連番が振られます。

厳密に欠番が出ないようにしたい場合のみ
INTEGER PRIMARY KEY AUTOINCREMENTを使います。


3. NOT NULL制約

NULL値の格納を禁止します。

CREATE TABLE users (
    id   INTEGER PRIMARY KEY,
    name TEXT NOT NULL,   -- NULLを禁止
    age  INTEGER          -- NULLを許可(デフォルト)
);
-- NULLを入れようとするとエラー
INSERT INTO users (name) VALUES (NULL);
-- Error: NOT NULL constraint failed: users.name

4. UNIQUE制約

重複した値の格納を禁止します。
NULLは複数格納できます(NULLはNULLと等しくないため)。

CREATE TABLE users (
    id    INTEGER PRIMARY KEY,
    email TEXT UNIQUE,    -- 重複禁止
    name  TEXT
);

-- テーブルレベルで複合UNIQUEも設定できる
CREATE TABLE schedules (
    user_id INTEGER,
    date    TEXT,
    UNIQUE (user_id, date) -- 同じユーザーの同じ日付は禁止
);

5. CHECK制約

指定した条件を満たす値のみ格納を許可します。

CREATE TABLE products (
    id    INTEGER PRIMARY KEY,
    name  TEXT    NOT NULL,
    price INTEGER CHECK (price >= 0),        -- 0以上のみ許可
    stock INTEGER CHECK (stock >= 0),        -- 0以上のみ許可
    rank  TEXT    CHECK (rank IN ('A','B','C')) -- 指定値のみ許可
);
-- 条件を満たさない値はエラー
INSERT INTO products (name, price) VALUES ('商品A', -100);
-- Error: CHECK constraint failed: products

6. DEFAULT制約

値が指定されなかった場合に使われるデフォルト値を設定します。

CREATE TABLE logs (
    id         INTEGER PRIMARY KEY,
    message    TEXT    NOT NULL,
    level      TEXT    DEFAULT 'INFO',                         -- 固定値
    created_at TEXT    DEFAULT (datetime('now', 'localtime')), -- 関数も使える
    is_read    INTEGER DEFAULT 0                               -- 0=未読
);
-- messageのみ指定するとlevel・created_at・is_readは自動設定
INSERT INTO logs (message) VALUES ('起動しました');

7. FOREIGN KEY(外部キー制約)

他テーブルの列を参照し、参照整合性を保ちます。

-- 親テーブル
CREATE TABLE parts (
    id   INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);

-- 子テーブル(外部キーあり)
CREATE TABLE orders (
    id      INTEGER PRIMARY KEY,
    part_id INTEGER NOT NULL,
    qty     INTEGER NOT NULL CHECK (qty > 0),
    FOREIGN KEY (part_id) REFERENCES parts(id)
        ON DELETE CASCADE  -- 親削除時に子も削除
        ON UPDATE CASCADE  -- 親更新時に子も更新
);

SQLiteの外部キー制約はデフォルトで無効です。
接続のたびに以下を実行して有効化してください。

PRAGMA foreign_keys = ON;

Pythonでは:

conn = sqlite3.connect("data.db")
conn.execute("PRAGMA foreign_keys = ON;")

8. 制約の組み合わせ例

実際のテーブルでは複数の制約を組み合わせて使います。

CREATE TABLE employees (
    id         INTEGER PRIMARY KEY,
    emp_code   TEXT    NOT NULL UNIQUE,           -- 社員コード(必須・重複禁止)
    name       TEXT    NOT NULL,                  -- 氏名(必須)
    department TEXT    NOT NULL DEFAULT '未配属',  -- 部署(必須・デフォルトあり)
    salary     INTEGER NOT NULL CHECK (salary >= 0), -- 給与(必須・0以上)
    created_at TEXT    DEFAULT (datetime('now', 'localtime'))
);

まとめ

制約 目的 注意点
PRIMARY KEY 行の一意識別 INTEGER PRIMARY KEYは自動採番
NOT NULL NULL禁止 デフォルトはNULL許可
UNIQUE 重複禁止 NULLは複数OK
CHECK 値の条件指定 複雑な条件も書ける
DEFAULT デフォルト値設定 関数も使える
FOREIGN KEY 参照整合性 有効化にPRAGMA foreign_keys = ONが必要

さいごに

テーブル制約を活用していないと
同じDBを扱う別のプログラムができた際、
プログラムごとに型チェックやNULL(None)になっていないか
等の確認処理が必要です。

テーブル制約を活用することで
それらの確認処理をDB側に行なってもらえるので
確認処理を省くことができ、冗長的なプログラムを防止できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?