データベース設計 制約とトリガ

データベース設計
制約とトリガ
講師: 福田 剛志
fukudat@fukudat.com
http://www.fukudat.com/
データベース設計
1
制約とトリガ (constraints and triggers)
• 制約 (constraint) とは,DBMSが強制するデー
タ項目間の関係
– 例) キー制約
• トリガ (trigger) とは,ある条件が成立したとき
(に限って)実行されるアクション
– 条件の例) タプルが挿入されたとき
– 制約よりも簡単に実装できる
データベース設計
2
制約の種類
• キー (key)
– 外部キー (foreign-key) または
参照整合性 (referential-integrity)
• 値に基づく制約 (value-based constraints)
– 属性の値に対する制約
• タプルに基づく制約 (tuple-based constraints)
– タプル内の要素間の制約
• 表明 (assertion)
– 任意の SQL boolean expression
データベース設計
3
外部キー (foreign key)
• リレーション 販売(飲み屋, 酒, 値段) について考
える
• 属性「酒」の値は実際に存在することが求められ
る.
– すなわち,酒(名前,製造元) リレーションの(名前属
性)に値として現れている.
• 「販売」リレーションの「酒」が「酒」リレーションの
「名前」でなければならないと言うような制約を
「外部キー制約」と言う
データベース設計
4
SQLでの外部キーの表現方法
• キーワード REFERENCES を用いて,
– 属性宣言の後に
REFERENCES <relation>(<attributes>)
をつけるか,
– テーブル要素として
FOREIGN KEY ( <list of attributes> )
REFERENCES <relation>( <attributes> )
• 参照される属性は,PRIMARY KEY か
UNIQUE と宣言されていなければならない.
データベース設計
5
例えば+: 属性宣言で
• CREATE TABLE 酒 (
名前
CHAR(20) PRIMARY KEY,
製造元 CHAR(20) );
• CREATE TABLE 販売 (
飲み屋 CHAR(20),
酒
CHAR(20)
REFERENCES 酒(名前),
値段
REAL );
データベース設計
6
例えば...: テーブル要素として
• CREATE TABLE 酒 (
名前
CHAR(20) PRIMARY KEY,
製造元 CHAR(20) );
• CREATE TABLE 販売 (
飲み屋 CHAR(20),
酒
CHAR(20)
値段
REAL,
FOREGN KEY 酒
REFERENCES 酒(名前) );
データベース設計
7
外部キー制約の強制
• リレーション R の属性からリレーション S の主
キー (primary key) に対する外部キー制約
(foreign-key constraint) があるとき,次の2種類
の制約違反(violation)が発生する可能性があ
る:
– R に対する挿入または更新によって,S にない値が
入力される.
– S に対する削除または更新によって,R が参照して
いた値がなくなる.
データベース設計
8
制約違反の対処法 (1)
• R = 販売, S = 酒 とする.
• 販売に対する挿入や更新で,酒に存在しない値
が入力された場合は,拒否する.
• 酒に対する削除や更新で,販売に存在する酒が
なくなった場合は,3通りの対処法がある.
– デフォルト (default): 変更を拒否する
– 連鎖 (cascade): 販売に対しても同じ変更を施す
• 酒の削除: 販売も削除する
• 酒の更新: 販売もその値に更新する
– 無効化 (set null): 販売の属性「酒」の値を NULL に
データベース設計
9
例えば+: 連鎖
• 酒リレーションから 名前 = ‘久保田千寿’ のタプ
ルを削除したなら
– 販売リレーションから,酒 = ‘久保田千寿’ を満たす
全てのタプルを削除する
• 酒リレーションの「久保田千寿」を「千寿」に変更
したなら
– 販売リレーションの 酒 = ‘久保田千寿’ を満たす全
てのタプルを 酒 = ‘千寿’ と変更する.
データベース設計
10
例えば+: 無効化
• 酒リレーションから 名前 = ‘久保田千寿’ のタプ
ルを削除したなら
– 販売リレーションから,酒 = ‘久保田千寿’ を満たす
全てのタプルを 酒 = NULL と変更する
• 酒リレーションの「久保田千寿」を「千寿」に変更
したなら
– 販売リレーションから,酒 = ‘久保田千寿’ を満たす
全てのタプルを 酒 = NULL と変更する
データベース設計
11
対処法の選択
• 外部キー制約を宣言するとき,対処法のポリ
シーを指定する.
ON [UPDATE | DELETE]
[SET NULL | CASCADE]
• どちらも指定しなかった場合は,デフォルト(拒否
する)
データベース設計
12
例えば+
• CREATE TABLE 販売 (
飲み屋 CHAR(20),
酒
CHAR(20),
値段
REAL,
FOREIGN KEY(酒)
REFERENCES 酒(名前)
ON DELETE SET NULL
ON UPDATE CASCADE
);
データベース設計
13
属性に基づく検査 (attribute-based check)
• 特定の属性の値に対して制約条件を付与する.
• 属性宣言の後に CHECK( <condition> ) を記
述する.
• この条件には,制約が付与される属性の名前を
用いることができるが,その他のリレーションや
属性名は副問合せの中でのみ使用できる.
データベース設計
14
例えば+
• CREATE TABLE 販売 (
飲み屋 CHAR(20),
酒
CHAR(20)
CHECK ( 酒 IN
(SELECT 名前 FROM 酒)),
値段
REAL
CHECK ( 値段 < 1000 )
);
データベース設計
15
検査のタイミング
• 属性に基づく検査は,その属性の値が挿入また
は更新されたときにだけ検査される.
– 例えば: CHECK (値段 < 1000) は値段に新しい値
が入力されるたびに検査され,1000円以上のとき拒
否する.
– 例えば: CHECK (酒 IN (SELECT 名前 FROM 酒))
は酒リレーションに対する削除では検査されない.
データベース設計
16
タプルに基づく検査 (tuple-based check)
• CHECK ( <condition> ) は一つのテーブル要素
として宣言することもできる.
• その場合,条件はそのリレーションの任意の属
性を参照することができる.
• ほかのリレーションの属性は,副問合せないで
のみ使用できる.
• 挿入と更新でのみ検査される.
データベース設計
17
例えば+
• 「高い店」だけが 1000円より高い酒を売ることが
できる:
• CREATE TABLE 販売 (
飲み屋 CHAR(20),
酒
CHAR(20),
値段
REAL,
CHECK (飲み屋 = ‘高い店’ OR
値段 <= 1000)
);
データベース設計
18
表明 (assertion)
• データベーススキーマの要素
• 定義方法:
CREATE ASSERTION <名前>
CHECK ( <条件> );
• 条件は任意のリレーションと属性を参照すること
ができる
データベース設計
19
例えば+ (1)
• リレーション 販売(飲み屋,酒,値段) において,どの店
の平均価格は500円以下でなければならないとする.
• CREATE ASSERTION ボッタクリ禁止
CHECK (
NOT EXISTS (
SELECT 飲み屋 FROM 販売
GROUP BY 飲み屋
HAVING 500 > AVG(値段)
)
);
平均価格が500円を超える飲み屋
データベース設計
20
例えば+ (2)
• リレーション 酒飲み(名前,住所,電話番号) と
飲み屋(名前,住所) において,飲み屋の数は酒
飲みの人数より少なくなければならないとする
• CREATE ASSERTION 数制限
CHECK (
(SELECT COUNT(*) FROM 飲み屋) <
(SELECT COUNT(*) FROM 酒飲み)
);
データベース設計
21
表明を検査するタイミング
• 原則としては,リレーションに変更があった後に
は,全ての表明を検査しなければならない.
• しかし,賢いシステムはどの種類の変更の後に
表明を検査すべきか判断できる.
– 例えば: 酒に対する変更は「数制限」に対する影響
はない.酒飲みに対する挿入も同じ
データベース設計
22
問題点
• 属性に基づく検査,タプルに基づく検査には限
界がある
• 表明はほとんどのアプリケーションに対して十分
な一般性があるが,効率的に実装することが難
しい
– DBMS は,違反する可能性のない表明を検査しな
いようにしなければならないが,簡単でない.
データベース設計
23
解決策: トリガ (trigger)
• トリガはユーザにいつ検査すべきか指定させる
• 表明のように,トリガには一般の条件を記述する
ことができ,任意の SQL による更新を実行する
ことができる.
データベース設計
24
イベント・条件・アクションルール
(event-condition-action rule)
• トリガはECAルール(event-condition-action
rule) とも言う
• イベント: データベースに対する変更の種類
– 例: 販売に対する挿入
• 条件: 任意の SQL条件式
• アクション: 任意の SQL文
データベース設計
25
例えば+
• 販売(飲み屋,酒,値段)に対して未知の酒が挿
入されたとき
• 外部キー制約を使って拒否するのではなく
• 酒(名前,製造元)リレーションにその酒を追加し
たい
– 製造元 = NULL にする
データベース設計
26
例えば+: 定義方法
イベント
• CREATE TRIGGER 酒トリガ
AFTER INSERT ON 販売
REFERENCING NEW ROW AS NewTuple
FOR EACH ROW
WHEN (NewTuple.酒 NOT IN
(SELECT 名前 FROM 酒))
INSERT INTO 酒(名前)
条件
VALUES(NewTuple.酒);
アクション
データベース設計
27
バリエーション (1)
• AFTER BEFORE.
– またリレーションがビューのときは INSTEAD OF が
使える
• ビューに対する変更をベーステーブルの変更に変換する方
法として使える
• INSERT DELETE または UPDATE.
– UPDATE はさらに UPDATE B ON <属性>
でも良い
データベース設計
28
バリエーション (2)
• トリガは行レベルか文レベルの2種類
• FOR EACH ROW をつけると,行レベルのトリ
ガとなる.つけなければ文レベル
• 行レベルのトリガは,タプルが変更されるたびに
1回実行される
• 文レベルのトリガは,一つのSQL文が(何行変
更するかにかかわらず)1回実行される
データベース設計
29
バリエーション (3)
• REFERENCING は
– INSERT 文の場合,新しいタプル (行レベルの場合
は 1つ; 文レベルの場合は タプルの集合) を表す.
– DELETE 文の場合は,削除されるタプルの集合
– UPDATE 文の場合は,変更前と変更後の両方
を [NEW OLD][TUPLE TABLE] AS <名前>
として参照するときに用いる
データベース設計
30
バリエーション (4)
• 条件には任意の条件式が書ける.
• 条件式はトリガのイベントが発生する前
(BEFORE) か後 (AFTER) に評価される
• REFERENCING 節で宣言された名前で,変更
によって影響されるタプルを参照することができ
る.
データベース設計
31
バリエーション (5)
• アクションには一つ以上の SQL文が書ける
– 複数文の場合はBEGIN と END で囲う
• アクションに問合せ文は意味がないので,更新
文に限られる.
データベース設計
32
別の例+
• 販売(飲み屋,酒,値段) に対して 100円以上の
値上げを行った飲み屋を ボッタクリ(飲み屋) と
して保持することを考える.
データベース設計
33
トリガの宣言
イベント
値段に対する変更があったとき
• CREATE TRIGGER 値段トリガ
AFTER UPDATE OF 値段 ON 販売
REFERENCING
変更前・後のタプルを参照
できるようにする
OLD ROW as old
NEW ROW as new 各行の変更を考慮する必要が
あるので,行レベル
FOR EACH ROW
WHEN(new.値段 >= old.値段 + 100)
INSERT INTO ボッタクリ
値段が100円以上,
値上がったか
VALUES(new.飲み屋);
アクション: ボッタクリに
値上げをした飲み屋を追加
データベース設計
34
ビューに対するトリガ
• 一般には,ビューは変更することができない.
• しかし INSTEAD OF トリガを使うと,ビューに対
する変更を意味のあるような変更に変換できる
• 例えば+: ビュー つながり(酒飲み,酒,飲み
屋) を,酒飲みが好きな酒を売っている,その酒
飲みの行きつけの飲み屋の関係を表すように定
義する.
データベース設計
35
ビューの定義
• CREATE VIEW つながり AS
SELECT 好き.酒飲み, 好き.酒, 販売.飲み屋
FROM 好き, 販売, 行きつけ
WHERE 好き.酒飲み = 行きつけ.酒飲み
AND 好き.酒 = 販売.酒
AND 販売.飲み屋 = 行きつけ.飲み屋
好き,販売,行きつけリレーションを自然結合
データベース設計
36
ビューの更新の解釈方法
• つながりリレーションに挿入することはできない
• しかし,INSTEAD OF トリガを用いて,
(酒飲み,酒,飲み屋) タプルを好き,販売,行き
つけに対する3つの挿入に変換することができる
– ただし,販売の値段はわからないのでデフォルト値
NULL とする
データベース設計
37
トリガの定義
• CREATE TRIGGER つながりトリガ
INSTEAD OF INSERT ON つながり
REFERENCING NEW ROW AS n
FOR EACH ROW
BEGIN
INSERT INTO 好き
VALUES(n.酒飲み, n.酒);
INSERT INTO 販売(飲み屋, 酒)
VALUES(n.飲み屋, n.酒);
INSERT INTO 行きつけ
VALUES(n.酒飲み, n.飲み屋);
END;
データベース設計
38