在關系型數據庫中,表與表之間存在引用關系,也就是說,數據列C1引用其他表的數據列C2中存在的值,引用關系通過外鍵(Foreign Key )約束實現。如果表(TableA)中的列C1被其他表引用,那么,我們把表(TableA)稱作參考表,或引用表(Referenced Table),該列C1是其他表的參考列,或引用列(Referenced Column),對引用列執行Update 或 Delete 操作會受到很多限制。某些情況下,也把參考表稱作父表,把引用父表的列稱作子列,或外鍵列。
[ FOREIGN KEY ] REFERENCES [ schema_name . ] referenced_table_name [ ( ref_column ) ] [ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] [ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ]
例如,在數據列C1和C2之間創建外鍵約束,C1是引用列,C2列引用C1列的值,外鍵約束的特點:
- 如果C2列的值不是NULL,那么C2列的值必須存在於C1列中,否則,系統返回違反外鍵約束的錯誤;
- 外鍵約束可以創建在同一個表的不同列之間,這種引用類型稱作自引用;
- 外鍵約束可以引用單列,也可以引用多列;
- 外鍵約束只能引用那些構成主鍵或唯一約束的數據列,或者唯一索引鍵列,也就是說,外鍵約束的引用列(單列或復合列)必須是唯一的;
一,外鍵約束的級聯操作
當引用列的值被更新(UPDATE或DELETE)時,子列會采取相應的動作:
- 默認行為是NO ACTION:如果子列引用父表的值,那么父表的更新操作被回滾,數據庫拋出錯誤;
- 級聯操作(CASCADE):當父表刪除數據行A時,子表刪除引用該數據行A的所有行;當父表更新數據行B時,子表把引用該數據行B的所有數據行都更新為新值;
- 置空(SET NULL):當父表更新數據行A時,子表把引用該數據行A的所有數據行都設置為NULL,該設置要求外鍵列必須可空(nullable);
- 設置默認值(SET DEFAULT):當父表更新數據時,子表把引用該數據行的數據行都設置為外鍵列的默認值,該設置要求外鍵列必須有默認值定義,如果構成外鍵的某一個列,沒有顯式定義默認值,並且可為NULL,那么系統把NULL作為列的默認值。
這些級聯操作是系統預定義的,不能更改。
二,手動更新外鍵
在對引用列進行更新操作(Update 和 Delete)之前,必須禁用外鍵約束,然后手動更新,最后再次啟用外鍵約束。手動更新外鍵,給用戶提供了很大的靈活性,由用戶保證外鍵約束的有效性。
1,查看外鍵列的信息
使用 sys.foreign_keys 和 sys.foreign_key_columns 這兩個系統表獲取外鍵約束的信息

select fk.object_id as FK_Object_ID, fk.name as FK_name, pt.name as ParentTable_Name, pc.name as ParentTable_Column_Name, rt.name as ReferencedTable_Name, rc.name as ReferencedTable_Column_Name from sys.foreign_keys fk with(nolock) inner join sys.foreign_key_columns fkc with(nolock) on fk.object_id=fkc.constraint_object_id inner join sys.tables pt with(nolock) on fkc.parent_object_id=pt.object_id inner join sys.columns pc with(nolock) on fkc.parent_object_id=pc.object_id and fkc.parent_column_id=pc.column_id inner join sys.tables as rt with(nolock) on fkc.referenced_object_id=rt.object_id inner join sys.columns rc with(nolock) on fkc.referenced_object_id=rc.object_id and fkc.referenced_column_id=rc.column_id where rt.name=N'Referenced_Table_name
2,禁用外鍵列
--disable FK constraint alter table ParentTable_name nocheck constraint FK_name
將外鍵 nocheck之后,可以使用 Delete 或 Update 命令更新數據,但是,當使用 Truncate 命令時,依然會報錯
Cannot truncate table 'xxx' because it is being referenced by a FOREIGN KEY constraint.
3,啟用外鍵列
--enable FK constraint alter table ParentTable_name check constraint FK_name
三,擴展引用完整性
系統自定義的外鍵約束,由很多限制,例如,引用列和外鍵列必須在相同的服務器上,相同的數據庫中,不支持跨數據庫的外鍵約束。而外鍵約束,實際上,就是一列引用另外一列的值,這個功能可以由觸發器來實現,用於擴展引用完整性。觸發器的實現過程是:創建INSTEAD OF觸發器,在插入數據之前,檢查插入的數據是否存在於參考表中,如果存在,插入成功;如果不存在,回滾事務,拋出異常。
CREATE TRIGGER schema_name.trigger_name ON table_name INSTEAD OF { [ INSERT ] [ , ] [ UPDATE ] } AS { sql_statement [ ; ] [ ,...n ] }
在DML觸發器中,有兩個特殊的系統表inserted和deleted,用於表示插入的新數據和刪除的舊數據。
參考文檔:
Use the inserted and deleted Tables
How can I list all foreign keys referencing a given table in SQL Server?