SQLite外键
SQLite外鍵(Foreign Key)支持
從SQLite 3.6.19 開始支持 外鍵約束. (Ubuntu 10.04 的SQLite版本是 3.6.22, ?Debian 6.0 的SQLite版本是 ?3.7.0)
外鍵約束用來強制 兩個表之間”存在”的關系. 比如, 考慮下面的SQL命令建立的schema
CREATE TABLE artist(
artistid INTEGER PRIMARY KEY,
artistname TEXT
);
CREATE TABLE track(
trackid INTEGER,
trackname TEXT,
trackartist INTEGER — 必須映射到一個 artist.artistid 記錄
);
使用這個數據庫的應用可以假定: 對于在track表里每一行, 都在artist表都存在一個對應的行. ? 不幸的是, 如果用戶使用外部工具編輯數據庫, 或者在應用程序中存在一個bug. 那么可能在track表中插入一行,而在artist表中沒有相應的記錄. 或者, 在artist表中刪除某些行, ?就會在track表里留下孤兒行(orphaned rows), ?它們在artist表中剩下的記錄找到任何對應的行. ?這可能在以后會導致應用的功能出錯. 或者至少讓編寫應用程序更復雜.
一個解決方法就是, 為數據庫添加一個外鍵約束,在artist和track這兩個表之間強制實施一個約束. 增加外鍵定義的track表的定義如下:
CREATE TABLE track(
trackid ? ? INTEGER,
trackname ? TEXT,
trackartist INTEGER,
FOREIGN KEY(trackartist) REFERENCES artist(artistid)
);
這樣,外鍵約束就由SQLite強制實施. ?往 track表插入一行 在 artist表中沒有對應的數據的記錄的企圖注定是要失敗的,o(∩∩)o
如果在track表還存在依賴于artist中的某行的記錄, 那么嘗試從 artist表刪除該行, 也會失敗.
也就是說, 對于在track表中的每一行,,下面的表達式都是真:
trackartist IS NULL OR EXISTS(SELECT 1 FROM artist WHERE artistid=trackartist)
在SQLite中啟用外鍵支持
1)為了在SQLite中使用外鍵約束, 編譯sqlite庫時, 不能使用 忽略 外鍵和觸發器, 也就是?SQLITE_OMIT_FOREIGN_KEY 和?SQLITE_OMIT_TRIGGER不能被定義
2)必須在運行時打開, 因為 ?默認是關閉的
PRAGMA foreign_keys = ON;
要求和建議的數據庫索引
通常, 外鍵約束的父鍵在父表里是主鍵. 如果它們不是主鍵, 那么父鍵欄 必須受一個UNIQUE約束 或者 有一個 UNIQUE 索引.
如果數據庫schema還有外鍵錯誤, 就需要查看多個表才能找到錯誤. 數據表創建時不會檢測這些錯誤,
這些錯誤會阻止應用程序 用SQL語句來修改子表或者父表的內容. 當內容被改變時,報告”DML errors”;當schema被改變時報告”DDL errors”
也就是說,錯誤地配置外鍵約束,要求檢查子表和父表的是DML錯誤, 一般顯示”foreign key mismatch” 或者 “no such table”
SELECT rowid FROM <child-table> WHERE <child-key> = :parent_key_value
如果這個SELECT返回數據, 那么SQLite就斷定, 從父表刪除某行, 將會違背外鍵約束,并返回錯誤. 如果父鍵的值被修改或者在父表插入新的一行,也會產生類似的查詢.
如果這些查詢沒有使用索引, 它們將強迫對整個子表做線性 查找(scan), 這代價可太大了
在大多數實際系統中, ?應該在子鍵這一欄建立索引.?子鍵的索引不必(并且是通常都不必) 有一個UNIQUE 索引(因為在子表中的多行對應于父表中的一行)
CREATE INDEX trackindex ON track(trackartist);ON DELETE 和 ON UPDATE行為
外鍵的ON DELETE和 ON ?UPDATE從句, 可以用來配置 ?當從父表中刪除 某些行時發生的行為(ON DELETE). ?或者 修改存在的行的父鍵的值, 發生的行為(ON UPDATE)
單個外鍵約束可以為ON DELETE和ON UPDATE配置不同的行為. ? 外鍵行為在很多時候類似于 觸發器(trigger)
ON DELETE和ON UPDATE的行為是 NO ACTION, ?RESTRICT, SET NULL, ?SET DEFAULT 或者 CASCADE
如果沒有明確指定星聞,那么默認就是NO ACTION
NO ACTION: 當父鍵被修改或者刪除時, 沒有特別的行為發生
RESTRICT: ?存在一個或者多個子鍵對應于相應的父鍵時, ?應用程序禁止刪除(ON DELETE RESTRICT)或者修改(ON UPDATE RESTRICT) 父鍵
RESTRICT與普通的外鍵約束的區別是, ?當字段(field)更新時, RESTRICT行為立即發生
SET NULL: 父鍵被刪除(ON DELETE SET NULL) 或者修改 (ON UPDATE SET NULL)
SET DEFAULT: 類似于SET NULL
CASCADE: 將實施在父鍵上的刪除或者更新操作,傳播給與之關聯的子鍵.
對于 ON DELETE CASCADE, 同被刪除的父表中的行 相關聯的子表中的每1行,也會被刪除.
對于ON UPDATE CASCADE, ?存儲在子表中的每1行,對應的字段的值會被自動修改成同新的父鍵匹配
舉例:
CREATE TABLE artist(
artistid ? ?INTEGER PRIMARY KEY,
artistname ?TEXT
);
CREATE TABLE track(
trackid ? ? INTEGER,
trackname ? TEXT,
trackartist INTEGER REFERENCES artist(artistid) ON UPDATE CASCADE
);
INSERT INTO artist VALUES(1, ‘Dean Martin’);
INSERT INTO artist VALUES(2, ‘Frank Sinatra’);
INSERT INTO track VALUES(14, ‘Mr. Bojangles’, 2);
INSERT INTO track VALUES(15, “That’s Amore”, 2);
INSERT INTO track VALUES(12, ‘Christmas Blues’, 1);
INSERT INTO track VALUES(13, ‘My Way’, 2);
sqlite> PRAGMA foreign_keys = ON;
(默認是關閉的, 要在運行時打開)
sqlite> SELECT * FROM artist;
1|Dean Martin
2|Frank Sinatra
sqlite> SELECT * FROM track;
14|Mr. Bojangles|2
15|That’s Amore|2
12|Christmas Blues|1
13|My Way|2
sqlite> UPDATE artist SET artistid = 999 WHERE artistname = ‘Dean Martin’;
(為 Dean Martin更改 artist表中的artistid欄目.
一般情況下, 這將 產生一個約束, 因為會讓 track表中的 一條記錄成為孤兒記錄
但 對外鍵定義使用了ON UPDATE CASCADE從句后, ?會把這個更新傳給 子表, 從而讓外鍵約束不被打破)
sqlite> SELECT * FROM artist;
2|Frank Sinatra
999|Dean Martin
sqlite> SELECT * FROM track;
14|Mr. Bojangles|2
15|That’s Amore|2
12|Christmas Blues|999
13|My Way|2
配置一個ON UPDATE或者ON DELETE行為并不意味著 外鍵約束 并不必要滿足.
舉例來說, 如果 配置了 ON DELETE SET DEFAULT 行為, 如果在父表中沒有 與子表欄目中默認值 相對應 的 行記錄, 當依賴的子鍵存在于子表中是, 刪除父鍵, 會破壞外鍵.
舉例:
sqlite> PRAGMA foreign_keys = ON;
CREATE TABLE artist(
artistid INTEGER PRIMARY KEY,
artistname TEXT
);
CREATE TABLE track(
trackid INTEGER,
trackname TEXT,
trackartist INTEGER DEFAULT 0 REFERENCES artist(artistid) ON DELETE SET DEFAULT
);
INSERT INTO artist VALUES(3, ‘Sammy Davis Jr.’);
INSERT INTO track VALUES(14, ‘Mr. Bojangles’, 3);
sqlite> DELETE FROM artist WHERE artistname = ‘Sammy Davis Jr.’;
Error: foreign key constraint failed
從父表中刪除一行, 會引起子表中相關的子鍵被設置成整數0
然而, 這個值不對應與 附表中的任何一行數據. 所以,外鍵約束被打破, 就拋出了異常
sqlite> INSERT INTO artist VALUES(0, ‘Unknown Artist’);
往父表中添加一行, 其主鍵為0
這樣刪除記錄就不會打破外鍵約束了
sqlite> DELETE FROM artist WHERE artistname = ‘Sammy Davis Jr.’;
sqlite> SELECT * FROM artist;
0|Unknown Artist
sqlite> SELECT * FROM track;
14|Mr. Bojangles|0
這寫都很類似于 SQLite 觸發器(triggers), ON DELETE SET DEFAULT行為, 在效果上, 同下面的 AFTER DELETE 觸發器是類似的
CREATE TRIGGER on_delete_set_default AFTER DELETE ON artist BEGIN
UPDATE child SET trackartist = 0 WHERE trackartist = old.artistid;
END;
外鍵約束的父表中的某行被刪除, 或者存儲在父鍵中的值 被修改時: 時間的邏輯順序是:
1. 執行在BEFORE 觸發器程序
2. 檢查本地(非外鍵)約束
3. 在父表中 更新或者刪除行
4. 執行要求的外鍵行為
5. 執行 AFTER觸發器 程序
在ON UPDATE外鍵行為 和 SQL 觸發器之間一個重要區別就是, ON UPDATE 行為只有在 父鍵的值 被修改并且父鍵的值修改得跟原來不一樣時,才執行.
如果下UPDATE SET 語句修改的值,跟原來一樣, ON UPDATE行為不會執行
?
轉載于:https://www.cnblogs.com/mafeng/p/8302215.html
總結
- 上一篇: scrapy的post登录:renren
- 下一篇: linux增加 路由使两个不同的网段可以