本文档介绍了SQLite 3.6.19(2009-10-14)版本中引入的对SQL外键约束的支持。
第一部分通过示例介绍了SQL外键的概念,并定义了文档其余部分使用的术语。第2节介绍了应用程序在SQLite中启用外键约束所必须采取的步骤(默认情况下处于禁用状态)。下一部分的第3节描述了用户必须创建的索引才能使用外键约束,以及为使外键约束有效运行而必须创建的索引。第4节介绍了SQLite支持的高级外键相关功能,第5节介绍了ALTER和DROP TABLE的方式命令得到了增强,以支持外键约束。最后,第6节列举了当前实现的缺失功能和局限性。
本文档未完整介绍用于在SQLite中创建外键约束的语法。这可以在CREATE TABLE语句的文档中找到。
SQL外键约束用于强制表之间的“存在”关系。例如,考虑使用以下SQL命令创建的数据库架构:
创建表艺术家( artistid整数主键, 艺术家名称TEXT ); 创建表轨道( trackid INTEGER, 追踪名称TEXT, trackartist INTEGER- 必须映射到artist.artistid! );
使用此数据库的应用程序有权假定,对于曲目表中的每一行,在艺术家表中都存在一个对应的行 。毕竟,宣言中的评论是这样说的。不幸的是,如果用户使用外部工具编辑数据库,或者应用程序中存在错误,则可能会将某些行插入到 音轨表中,而这些行与Artist 表中的任何行都不对应。或行可能会从被删除艺术家表之后,离开孤立行轨迹不对应于任何在剩余行的表艺术家。这可能会导致该应用程序以后发生故障,或者至少使该应用程序的编码更加困难。
一种解决方案是将SQL外键约束添加到数据库架构中,以加强艺术家与 曲目表之间的关系。为此,可以通过将跟踪表的声明修改为以下内容来添加外键定义:
创建表轨道( trackid INTEGER, 追踪名称TEXT, 田径运动员INTEGER, 外国钥匙(田径艺术家)参考艺术家(artistid) );
这样,约束由SQLite强制执行。尝试在轨道表中插入与艺术家表中任何行都不对应的行将失败,当轨道表中存在相关行时,尝试从艺术家表中删除行也会失败。有一个例外:如果轨道表中的外键列为NULL,则在artist表中不需要相应的条目。用SQL表示,这意味着对于跟踪表中的每一行,以下表达式的计算结果为true:
trackartist为NULL或存在(从艺术家WHERE artistid = trackartist中选择1个)
提示:如果应用程序要求artist和track之间有更严格的关系 ,而trackartist列中不允许使用NULL值,则只需将适当的“ NOT NULL”约束添加到架构。
还有其他几种方法可以将等效的外键声明添加到CREATE TABLE语句中。有关详细信息,请参考 CREATE TABLE文档。
以下SQLite命令行会话说明了将外键约束添加到跟踪表的效果:
sqlite> SELECT * FROM artist; artistid artistname -------- ----------------- 1迪恩·马丁(Dean Martin) 2弗兰克·辛纳屈 sqlite> SELECT * FROM轨道; trackid trackname trackartist ------- ----------------- ----------- 11那是爱茉莉1 12圣诞蓝调1 13我的方式2 sqlite> -失败,因为插入trackartist列(3) sqlite>中的值不对应于artist表中的行。 sqlite> INSERT INTO跟踪VALUES(14,'Mr. Bojangles',3); SQL错误:外键约束失败 sqlite> -之所以成功,是因为在trackartist中插入了NULL。一个 源码> -在艺术家表中的相应行不会在这种情况下需要。 sqlite> INSERT INTO跟踪VALUES(14,'Mr. Bojangles',NULL); 源码> -试图修改记录的trackartist领域具有后 sqlite的> -插入也不管用,因为trackartist(3)新的价值 源码> -不过不对应任何行中的艺术家桌子。 sqlite> UPDATE track SET trackartist = 3 WHERE trackname ='Mr. Bojangles'; SQL错误:外键约束失败 sqlite> -将所需的行插入到artist表中。然后可以进行 sqlite> -更新插入的行以将trackartist设置为3(因为现在存在artist表中的相应 sqlite> -行)。 sqlite> INSERT INTO artist VALUES(3,'Sammy Davis Jr.'); sqlite> UPDATE track SET trackartist = 3 WHERE trackname ='Mr. Bojangles'; sqlite> -现在说“小萨米·戴维斯(Sammy Davis Jr.)”。(artistid = 3)已添加到数据库中, sqlite> -可以使用该艺术家插入新曲目而不会违反 sqlite> -外键约束: sqlite>插入跟踪值(15,'Boogie Woogie',3);
如您所料,不可能通过删除或更新artist表中的行来将数据库操作到违反外键约束的状态 :
sqlite> -尝试删除“ Frank Sinatra”的艺术家记录失败,因为 sqlite> -曲目表中包含引用该行的行。 sqlite>从艺术家中删除WHERE artistname ='Frank Sinatra'; SQL错误:外键约束失败 sqlite> -从曲目表中删除所有引用艺术家 sqlite> -“ Frank Sinatra”的记录。只有这样,才可以删除艺术家。 sqlite>从轨道WHERE轨道名称中删除'='我的方式'; sqlite>从艺术家中删除WHERE artistname ='Frank Sinatra'; sqlite> -尝试更新artist表中某一行的artistid,而 sqlite> -在跟踪表中存在引用该行的记录。 sqlite> UPDATE artist SET artistid = 4 WHERE artistname ='Dean Martin'; SQL错误:外键约束失败 源码> -一旦所有引用的行中的艺术家表中的记录具有 源码> -被删除,但可以修改该行的artistid。 sqlite>从曲目WHERE曲目名称IN('That's Amore','Christmas Blues')删除; sqlite> UPDATE artist SET artistid = 4 WHERE artistname ='Dean Martin';
该父表是表的外键约束是指。本节示例中的父表是 Artist表。一些书籍和文章将其称为 参考表,可以说是更正确的表,但容易引起混淆。
该子表是一个外键约束应用于表和包含参考条款表。本节中的示例将跟踪表用作子表。其他书籍和文章将此称为 参考表。
该父键是列在父表中的列或组的外键约束是指。这通常是父表的主键,但并非总是如此。父键必须是父表中的一个或多个命名列,而不是rowid。
该子键是列或由外键约束的约束和子表列集按住参考条款。
如果对于子表中的每一行,一个或多个子键列为NULL,或者在父表中存在一行,而每个父键列包含的值等于其子表中的值,则满足外键约束。关联的子键列。
在以上段落中,术语“等于”是指使用此处指定的规则比较值时等于“相等” 。以下澄清适用:
为了在SQLite中使用外键约束,必须在未定义SQLITE_OMIT_FOREIGN_KEY或 SQLITE_OMIT_TRIGGER的情况下编译库。如果定义了SQLITE_OMIT_TRIGGER但未定义SQLITE_OMIT_FOREIGN_KEY,则SQLite的行为与版本3.6.19(2009-10-14)之前的行为相同-将解析外键定义,并可以使用PRAGMA foreign_key_list对其进行查询,但不会强制执行外键约束。在此配置中,PRAGMA foreign_keys命令是no-op。如果定义了OMIT_FOREIGN_KEY,则甚至无法解析外键定义(试图指定外键定义是语法错误)。
假设库是在启用了外键约束的情况下编译的,则它仍必须由应用程序在运行时使用PRAGMA foreign_keys命令启用 。例如:
sqlite> PRAGMA foreign_keys =开;
默认情况下,外键约束是禁用的(为了向后兼容),因此必须分别为每个数据库连接启用外键约束。(但是请注意,SQLite的未来版本可能会更改,从而默认情况下会启用外键约束。谨慎的开发人员不会对默认情况下是否启用外键做出任何假设,而是根据需要启用或禁用它们。)应用程序还可以使用PRAGMA foreign_keys语句来确定当前是否启用了外键。以下命令行会话对此进行了演示:
sqlite> PRAGMA外键; 0 sqlite> PRAGMA foreign_keys =开; sqlite> PRAGMA外键; 1个 sqlite> PRAGMA外键= OFF; sqlite> PRAGMA外键; 0
提示:如果命令“ PRAGMA foreign_keys”不返回任何数据,而不是包含“ 0”或“ 1”的单行,则您正在使用的SQLite版本不支持外键(因为它早于3.6.19或因为它是使用已定义的SQLITE_OMIT_FOREIGN_KEY或 SQLITE_OMIT_TRIGGER编译的)。
在多语句事务的中间(SQLite未处于自动提交模式时),不能启用或禁用外键约束。尝试这样做不会返回错误。它根本没有效果。
通常,外键约束的父键是父表的主键。如果它们不是主键,则父键列必须共同受UNIQUE约束或具有UNIQUE索引。如果父键列具有UNIQUE索引,则该索引必须使用在CREATE TABLE语句中为父表指定的排序规则序列。例如,
CREATE TABLE parent(一个主键,b个唯一,c,d,e,f); 在父级(c,d)上创建唯一索引i1; 在父级(e)上创建索引i2; 在父级上创建唯一索引i3(f COLLATE nocase); 创建表child1(f,g参考parent(a)); -确定 创建表child2(h,i REFERENCES parent(b)); -确定 创建表child3(j,k,FOREIGN KEY(j,k)REFERENCES parent(c,d)); -确定 创建表child4(l,m REFERENCES parent(e)); - 错误! 创建表child5(n,o参考parent(f)); - 错误! 创建表child6(p,q,FOREIGN KEY(p,q)参考parent(b,c)); - 错误! 创建表child7(r引用parent(c)); - 错误!
作为表的一部分创建的外键约束child1, 的child2和child3无一不精。声明为表child4一部分的外键是错误的,因为即使父键列已建立索引,但该索引也不是UNIQUE。表child5的外键 是一个错误,因为即使父键列具有唯一索引,索引也使用不同的整理顺序。表child6和child7是不正确的,因为它们的父键上都具有UNIQUE索引,但这些键与单个UNIQUE索引的列并不完全匹配。
如果数据库模式包含需要查看多个表定义以进行标识的外键错误,则在创建表时将不会检测到这些错误。而是,此类错误阻止应用程序准备使用外键来修改子表或父表的内容的SQL语句。更改内容时报告的错误为“ DML错误”,更改架构时报告的错误为“ DDL错误”。因此,换句话说,需要同时查看子级和父级的错误配置的外键约束是DML错误。外键DML错误的英语错误消息通常是“外键不匹配”,但如果父表不存在,也可以是“ no such table”。
上面的最后一个项目符号如下所示:
创建表parent2(a,b,PRIMARY KEY(a,b)); 创建表child8(x,y,FOREIGN KEY(x,y)参考parent2); -确定 创建表child9(x参考parent2); - 错误! 创建表child10(x,y,z,FOREIGN KEY(x,y,z)参考parent2); - 错误!
相反,如果仅通过查看子表的定义即可识别外键错误,而不必查阅父表的定义,则子表的 CREATE TABLE语句将失败。因为该错误发生在架构更改期间,所以这是DDL错误。无论创建表时是否启用了外键约束,都会报告外键DDL错误。
子键列不需要索引,但是它们几乎总是有益的。返回第1节中的示例,每当应用程序从艺术家表(父表)中删除一行时,它都执行与以下SELECT语句等效的操作,以搜索轨道表(子表)中的引用行。
从轨道WHERE trackartist =中选择行ID?
在哪里 ?上面的中的替换 为从艺术家表中删除的记录的artistid列的值(回想一下trackartist列是子项,而artistid 列是父项)。或者,更一般而言:
在<child-table>中选择ROWID <WHERE <child-key> =:parent_key_value
如果此SELECT根本不返回任何行,则SQLite得出结论,从父表中删除该行将违反外键约束,并返回错误。如果修改了父键的内容或在父表中插入了新行,则可能会运行类似的查询。如果这些查询不能使用索引,则它们将被迫对整个子表进行线性扫描。在非平凡的数据库中,这可能会非常昂贵。
因此,在大多数实际系统中,应该在每个外键约束的子键列上创建索引。子键索引不必是(通常也不会)是UNIQUE索引。再次回到第1节中的示例,有效执行外键约束的完整数据库模式可能是:
创建表艺术家( artistid整数主键, 艺术家名称TEXT ); 创建表轨道( trackid INTEGER, 追踪名称TEXT, trackartist整数参考艺术家 ); CREATE INDEX trackindex在track(trackartist)上;
上面的块使用简写形式来创建外键约束。在列定义上附加“ REFERENCES <parent-table> ”子句会创建一个外键约束,该约束将列映射到<parent-table>的主键。 有关更多详细信息,请参考CREATE TABLE文档。
组合外键约束是子键和父键都是组合键的约束。例如,考虑以下数据库架构:
创建表相册( Albumartist TEXT, 专辑名称TEXT, 专辑封面BINARY, 主键(相册,专辑名称) ); CREATE TABLE歌曲( songid INTEGER, Songartist TEXT, Songalbum TEXT, 歌曲名TEXT, 外键(歌手,歌手)参考专辑(专辑,专辑名称) );
在此系统中,要求歌曲表中的每个条目都映射到专辑表中具有艺术家和专辑的相同组合的条目。
父密钥和子密钥必须具有相同的基数。在SQLite中,如果任何子键列(在这种情况下为songartist和songalbum)为NULL,则无需在父表中有相应的行。
SQLite中的每个外键约束都分为即时或延迟。默认情况下,外键约束是即时的。到目前为止提供的所有外键示例都具有直接的外键约束。
如果语句修改了数据库的内容,从而在该语句结束时违反了即时外键约束,则将引发异常,并还原该语句的作用。相反,如果一条语句修改了数据库的内容,从而违反了延迟的外键约束,则不会立即报告该违反。在事务尝试提交之前,不检查延迟的外键约束。只要用户有未清事务,就允许数据库以违反任何数量的延迟外键约束的状态存在。但是,只要仍然违反外键约束, COMMIT就会失败。
如果当前语句不在显式事务( BEGIN / COMMIT / ROLLBACK块)内,则在语句执行完成后立即提交隐式事务。在这种情况下,延迟约束的行为与立即约束相同。
要将外键约束标记为延迟,其声明必须包含以下子句:
可延期的初始 延期-延期的外键约束
CREATE TABLE文档的 一部分提供了用于指定外键约束的完整语法。用以下任何一项替换上面的短语会创建立即外键约束。
NOT DEFERRABLE INITIALLY DEFERRED -一个直接的外键约束 不可推迟INITIALLY IMMEDIATE 一个直接的外键约束- 不可推迟 -一个直接的外键约束 DEFERRABLE INITIALLY IMMEDIATE -一个直接的外键约束 DEFERRABLE -一个直接的外键约束
该编译defer_foreign_keys可以用来临时改变所有的外键约束递延不管它们是如何宣布。
以下示例说明了使用延迟外键约束的效果。
-数据库架构。两个表最初都是空的。 创建表艺术家( artistid整数主键, 艺术家名称TEXT ); 创建表轨道( trackid INTEGER, 追踪名称TEXT, trackartist INTEGER参考艺术家(artistid)DEFERRABLE INITIALLY DEFERRED ); sqlite3> -如果外键约束是立即的,则此INSERT将 sqlite3> -导致错误(因为在table artist中没有行带有 sqlite3> -artistid = 5)。但是由于推迟了约束并且有一个 sqlite3> -打开事务,所以不会发生错误。 sqlite3> BEGIN; sqlite3>插入跟踪值(1,“白色圣诞节”,5); sqlite3> -以下COMMIT失败,因为数据库处于 sqlite3> -不满足延迟的外键约束的状态。在 sqlite3的> -事务保持打开状态。 sqlite3> COMMIT; SQL错误:外键约束失败 sqlite3> -在artistID = 5的artist表中插入一行后, sqlite3> -延迟的外键约束得到满足。然后有可能 sqlite3> -提交事务而没有错误。 sqlite3>插入艺术家VALUES(5,'Bing Crosby'); sqlite3> COMMIT;
一个嵌套的保存点,而数据库是在不满足延期外键约束的状态事务可能被释放。另一方面,事务保存点(当前没有打开的事务时打开的非嵌套保存点)受与COMMIT相同的限制-尝试在数据库处于这种状态时释放它失败。
如果COMMIT语句(或事务SAVEPOINT的RELEASE)由于数据库当前处于违反延迟的外键约束的状态而失败,并且当前存在嵌套的保存点,则失败 ,则嵌套的保存点将保持打开状态。
外键ON DELETE和ON UPDATE子句用于配置从父表中删除行(ON DELETE)或修改现有行的父键值(ON UPDATE)时发生的操作。单个外键约束可能具有为ON DELETE和ON UPDATE配置的不同操作。外键动作在许多方面都类似于触发器。
与SQLite数据库中每个外键关联的ON DELETE和ON UPDATE操作是“ NO ACTION”,“ RESTRICT”,“ SET NULL”,“ SET DEFAULT”或“ CASCADE”之一。如果未明确指定动作,则默认为“ NO ACTION”。
NO ACTION:配置“ NO ACTION”仅意味着:当修改或删除数据库中的父键时,不会执行任何特殊操作。
限制:“ RESTRICT”操作意味着禁止应用程序删除(对于ON DELETE RESTRICT)或修改(对于ON UPDATE RESTRICT)存在一个或多个子键的父键。RESTRICT动作与常规外键约束执行的效果之间的区别在于,RESTRICT动作处理在字段更新后立即进行-而不是像立即约束那样在当前语句的末尾或结尾处进行与延迟约束相同的当前交易额。即使推迟了附加的外键约束,如果删除或修改了具有相关子键的父键,配置RESTRICT操作也会导致SQLite立即返回错误。
SET NULL:如果配置的操作为“ SET NULL”,则当删除父键(对于ON DELETE SET NULL)或修改父键(对于ON UPDATE SET NULL)时,映射的子表中所有行的子键列设置为包含父键的SQL NULL值。
SET DEFAULT:“ SET DEFAULT”操作与“ SET NULL”类似,不同之处在于,每个子键列均设置为包含列的默认值而不是NULL。 有关如何将默认值分配给表列的详细信息,请参考CREATE TABLE文档。
CASCADE:“ CASCADE”动作将对父键的删除或更新操作传播到每个从属子键。对于“ ON DELETE CASCADE”操作,这意味着子表中与已删除的父行关联的每一行也将被删除。对于“ ON UPDATE CASCADE”操作,这意味着修改存储在每个从属子键中的值以匹配新的父键值。
例如,如下所示,向外键添加“ ON UPDATE CASCADE”子句可增强第1节中的示例架构,以允许用户在不破坏参照完整性的情况下更新artistid(外键约束的父键)列:
-数据库架构 创建表艺术家( artistid整数主键, 艺术家名称TEXT ); 创建表轨道( trackid INTEGER, 追踪名称TEXT, trackartist整数参考artist(artistid)在更新级联上 ); sqlite> SELECT * FROM artist; artistid artistname -------- ----------------- 1迪恩·马丁(Dean Martin) 2弗兰克·辛纳屈 sqlite> SELECT * FROM轨道; trackid trackname trackartist ------- ----------------- ----------- 11那是爱茉莉1 12圣诞蓝调1 13我的方式2 sqlite> -更新“ Dean Martin”的艺术家记录的artistid列。 sqlite> -通常,这会产生约束,因为它将孤立轨道表中的两个 sqlite> -依赖记录。但是,附加到外键定义的ON UPDATE CASCADE子句 sqlite>导致对子表的更新为“级联” sqlite> -子表,从而防止了外键约束冲突。 sqlite>更新艺术家SET artistid = 100 WHERE artistname ='Dean Martin'; sqlite> SELECT * FROM artist; artistid artistname -------- ----------------- 2弗兰克·辛纳屈 100迪恩·马丁 sqlite> SELECT * FROM轨道; trackid trackname trackartist ------- ----------------- ----------- 11那是爱茉莉100 12圣诞蓝调100 13我的方式2
配置ON UPDATE或ON DELETE操作并不意味着不需要满足外键约束。例如,如果配置了“ ON DELETE SET DEFAULT”操作,但是父表中没有与子键列的默认值相对应的行,则在存在依赖的子键时删除父键仍然会导致外键违反。例如:
-数据库架构 创建表艺术家( artistid整数主键, 艺术家名称TEXT ); 创建表轨道( trackid INTEGER, 追踪名称TEXT, trackartist INTEGER DEFAULT 0参考artist(artistid)在DELETE SET DEFAULT上 ); sqlite> SELECT * FROM artist; artistid artistname -------- ----------------- 3萨米·戴维斯(Sammy Davis Jr. sqlite> SELECT * FROM轨道; trackid trackname trackartist ------- ----------------- ----------- 14 Bojangles先生3 sqlite> -从父表中删除该行会导致子键 sqlite> -从属行的值设置为整数值0。但是,此 sqlite> -值与父表中的任何行都不对应。因此, sqlite> -违反了外键约束,并引发了is异常。 sqlite>从艺术家那里删除artistname ='Sammy Davis Jr.'; SQL错误:外键约束失败 sqlite> -这次,值0确实对应于父表行。还有 sqlite> -这样DELETE语句就不会违反外键约束 sqlite> -并且不会抛出异常。 sqlite> INSERT INTO artist VALUES(0,'Unknown Artist'); sqlite>从艺术家那里删除artistname ='Sammy Davis Jr.'; sqlite> SELECT * FROM artist; artistid artistname -------- ----------------- 0未知艺术家 sqlite> SELECT * FROM轨道; trackid trackname trackartist ------- ----------------- ----------- 14 Bojangles先生0
那些熟悉SQLite触发器的人 会注意到,上面示例中演示的“ ON DELETE SET DEFAULT”操作实际上与以下AFTER DELETE触发器类似:
CREATE TRIGGER on_delete_set_default在艺术家开始之后DELETE ON UPDATE child SET trackartist = 0 WHERE trackartist = old.artistid; 结尾;
每当删除外键约束的父表中的一行,或者修改存储在一个或多个父键列中的值时,事件的逻辑顺序为:
ON UPDATE外键操作和SQL触发器之间有一个重要区别。仅在修改了父键的值以使新的父键值不等于旧的键值时,才执行ON UPDATE操作。例如:
-数据库架构 CREATE TABLE parent(x主键); CREATE TABLE child(y REFENTENCES parent ON UPDATE SET NULL); sqlite> SELECT * FROM父级; X - 钥匙 sqlite> SELECT * FROM child; ÿ - 钥匙 sqlite> -由于以下UPDATE语句实际上并未修改 sqlite> -父键值,因此不会执行ON UPDATE操作,并且 sqlite> -子键值未设置为NULL。 sqlite> UPDATE父SET x ='key'; sqlite> SELECT IFNULL(y,'null')FROM child; ÿ - 钥匙 sqlite> -这次,由于UPDATE语句确实修改了父键 sqlite> -值,因此将执行ON UPDATE操作,并将子键设置为 sqlite> -为NULL。 sqlite> UPDATE父SET x ='key2'; sqlite> SELECT IFNULL(y,'null')FROM child; ÿ - 空值
本节介绍CREATE TABLE,ALTER TABLE和DROP TABLE命令与SQLite的外键交互的方式。
一个CREATE TABLE命令操作相同与否 外键约束已启用。创建表时,不检查外键约束的父键定义。没有什么可以阻止用户创建一个外键定义,该外键定义引用一个不存在的父表,或者引用一个不存在或未由PRIMARY KEY或UNIQUE约束共同绑定的父键列。
启用外键约束时 ,ALTER TABLE命令在两个方面的工作方式有所不同:
除非新列的默认值为NULL,否则无法使用“ ALTER TABLE ... ADD COLUMN”语法添加包含REFERENCES子句的列。尝试这样做会返回错误。
如果使用“ ALTER TABLE ... RENAME TO”命令重命名作为一个或多个外键约束的父表的表,则外键约束的定义将修改为以其新名称引用父表。修改了子CREATE TABLE语句或存储在sqlite_schema表中的语句的文本,以反映新的父表名称。
如果在准备表时启用了外键约束,则 DROP TABLE命令将执行隐式DELETE,以在删除表之前从表中删除所有行。隐式DELETE不会引发任何SQL触发器,但可能会调用外键动作或违反约束的行为。如果违反了即时外键约束,则DROP TABLE语句将失败,并且不会删除该表。如果违反了延迟的外键约束,则如果此时仍存在外键约束冲突,则用户尝试提交事务时将报告错误。作为隐式DELETE的一部分遇到的任何“外键不匹配”错误都将被忽略。
这些对ALTER TABLE和DROP TABLE 命令的增强的目的是确保至少在启用外键约束的情况下,不能将它们用于创建包含外键违规的数据库。但是,此规则有一个例外。如果父键不受作为父表定义一部分创建的PRIMARY KEY或UNIQUE约束的约束,但是由于使用CREATE INDEX创建的索引而受UNIQUE约束的约束命令,然后可以填充子表而不会引起“外键不匹配”错误。如果从数据库架构中删除了UNIQUE索引,那么父表本身也将被删除,则不会报告任何错误。但是,数据库可能处于外键约束的子表包含不引用任何父表行的行的状态。如果数据库架构中的所有父键都受作为父表定义一部分添加的PRIMARY KEY或UNIQUE约束(而不是由外部UNIQUE索引)约束,则可以避免这种情况。
上述DROP TABLE和ALTER TABLE命令的属性仅在启用外键的情况下适用。如果用户认为它们不受欢迎,则解决方法是在执行DROP或ALTER TABLE命令之前,使用PRAGMA foreign_keys禁用外键约束。当然,在禁用外键约束的同时,没有什么可以阻止用户违反外键约束并因此创建内部不一致的数据库。
本节列出了一些限制和省略的功能,这些功能在其他地方没有提到。
不支持MATCH子句。根据SQL92,可以将MATCH子句附加到复合外键定义,以修改处理子键中出现的NULL值的方式。如果指定了“ MATCH SIMPLE”,则如果一个或多个子键值为NULL,则不需要子键对应于父表的任何行。如果指定了“ MATCH FULL”,则如果任何子键值是NULL,则父表中不需要相应的行,但是所有子键值都必须是NULL。最后,如果将外键约束声明为“ MATCH PARTIAL”,并且子键值之一为NULL,则在父表中必须至少存在一行,其中非NULL子键值与父键值匹配。
SQLite解析MATCH子句(即,如果指定一个子句,则不报告语法错误),但不强制执行它们。SQLite中的所有外键约束都像指定了MATCH SIMPLE一样进行处理。
不支持在延迟模式和立即模式之间切换约束。许多系统允许用户在运行时在延迟模式和立即模式之间切换各个外键约束(例如,使用Oracle“ SET CONSTRAINT”命令)。SQLite不支持此功能。在SQLite中,外键约束在创建时会永久标记为“延迟”或“立即”。
外键动作的递归限制。该 SQLITE_MAX_TRIGGER_DEPTH和SQLITE_LIMIT_TRIGGER_DEPTH 设置确定触发递归程序所允许的最大深度。出于这些限制的目的, 外键动作被视为触发程序。该 PRAGMA RECURSIVE_TRIGGERS设置不影响外键操作的操作。无法禁用递归外键操作。