外鍵

外來鍵又稱外部鍵，是指在關聯式資料庫中，每個資料表都是由關聯來連繫彼此的關係，父資料表（Parent Entity）的主鍵（Primary Key）會放在另一個資料表，當做屬性以建立彼此的關聯，而這個屬性就是外來鍵。

比如，學生跟老師之間是教學的關聯，學生資料表會有個屬性叫指導老師(FK)，而這個值就是對應到老師資料表的老師代號(PK)，學生的指導老師就是外來鍵。

外鍵是表中的一組特性，它引用另一個表的主鍵。外鍵連結這兩個表。另一種說法：在關係資料庫的上下文中，外鍵是一組受某種包含依賴約束的特性，特別是由一個關係R中的外鍵特性組成的元組必須也存在於其他一些（不一定是不同的）關係S中，而且這些屬性也必須是S中的候選鍵。簡而言之，外鍵是一組引用候選鍵的特性。例如，名為TEAM的表可能有一個屬性 MEMBER_NAME，它引用PERSON表中的候選鍵PERSON_NAME作為外鍵。由於MEMBER_NAME是一個外鍵，任何在TEAM中作為成員名稱存在的值也必須作為人名存在於PERSON表中；換句話說，一個團隊的每個成員也是一個人。

## 概述

包含外鍵的表稱為子表(child table)，包含候選鍵的表稱為「被引用表」(referenced )或父表(parent table)。在資料庫關係建模和實現中，候選鍵是一組零個或多個特性，其值對於關係中的每個元組（行）保證是唯一的。任何元組的候選鍵特性的值或值組合不能與該關係中的任何其他元組重複。

由於外鍵的目的是標識被引用表的某一行，所以一般要求外鍵等於主表某行的候選鍵，否則沒有值（NULL）。此規則稱為兩個表之間的參照完整性約束。因為違反這些約束可能是許多資料庫問題的根源，所以大多數資料庫管理系統提供了確保每個非空外鍵對應於引用表的一行的機制。

例如，考慮一個包含兩個表的資料庫：一個包含所有客戶數據的 CUSTOMER 表和一個包含所有客戶訂單的 ORDER 表。假設業務要求每個訂單必須指向一個客戶。為了在資料庫中反映這一點，在 ORDER 表中添加了一個外鍵列（例如 CUSTOMERID），它引用了 CUSTOMER 的主鍵（例如 ID）。因為表的主鍵必須是唯一的，並且因為 CUSTOMERID 僅包含來自該主鍵欄位的值，所以我們可以假設，當它具有值時，CUSTOMERID 將標識出下訂單的特定客戶。但是，如果在刪除 CUSTOMER 表的行或更改 ID 列時 ORDER 表沒有保持最新，則上述約束不再保證成立，並且使用這些表可能會變得更加困難。許多現實世界的資料庫通過「停用」而不是物理刪除主表外鍵，或者通過複雜的更新程序來解決這個問題，這些程序在需要更改時修改對外鍵的所有引用。

外鍵在資料庫設計中起著至關重要的作用。資料庫設計的一個重要部分是確保真實世界實體之間的關係通過引用反映在資料庫中，使用外鍵從一個表引用到另一個表。資料庫設計的另一個重要部分是資料庫規範化，其中表被分解並且外鍵使得它們可以被重構。

引用表（或子表）中的多行可能引用到被引用表（或父表）中的同一行。在這種情況下，兩個表之間的關係稱為引用表和被引用表之間的一對多關係。

此外，實際上，子表和父表可能是同一張表，即外鍵指向同一張表。這種外鍵在 SQL:2003 中稱為自引用(self-referencing)或遞歸外鍵(recursive foreign key)。在資料庫管理系統中，這通常是通過將第一個和第二個引用連結到同一個表來完成的。

一個表可能有多個外鍵，每個外鍵可以有不同的父表。每個外鍵都由資料庫系統獨立執行。因此，可以使用外鍵建立表之間的級聯關係。

外鍵定義為關係中的一個特性或一組特性，其值與另一個關係中的主鍵匹配。將此類約束添加到現有表的語法在 SQL:2003 中定義，如下所示。

```sql
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY(外键字段名)
REFERENCES 外表表名(主键字段名)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
```

在`REFERENCES`子句中省略列列表意味著外鍵應引用到被引用表的主鍵。同樣，外鍵可以定義為`CREATE TABLE`語句的一部分。

```sql
CREATE TABLE child_table (
  col1 INTEGER PRIMARY KEY,
  col2 CHARACTER VARYING(20),
  col3 INTEGER,
  col4 INTEGER,
  FOREIGN KEY(col3, col4) REFERENCES parent_table(col1, col2) ON DELETE CASCADE
)
```

如果外鍵是一個單獨列，則可以使用下屬語法:

```sql
CREATE TABLE child_table (
  col1 INTEGER PRIMARY KEY,
  col2 CHARACTER VARYING(20),
  col3 INTEGER,
  col4 INTEGER REFERENCES parent_table(col1) ON DELETE CASCADE
)
```

外鍵也可以用存儲過程語句定義：

```sql
sp_foreignkey child_table, parent_table, col3, col4
```

- parent_table: 該候選鍵必須已經定義了
- col3 和 col4: 外鍵的名字。包括1列至8列。

## 引用動作

在對主表做行刪除或修改時，為確保引用完整性，SQL:2003給出了5種「引用動作（referential actions）：

- CASCADE
- RESTRICT
- NO ACTION
- SET NULL
- SET DEFAULT

### CASCADE

在對主表的行刪除或修改時，從表中有匹配的外鍵值的行也相應被刪除或修改。

ON DELETE CASCADE修改了從表的行為，引入了對從表的自動刪除或修改的行為。

### RESTRICT

在對主表的行刪除或修改時，如果子表中有匹配的外鍵值的行，則主表中相應的行不能被刪除或修改。

ON DELETE RESTRICT是對主表的行為約束，雖然寫在了從表的定義中。

RESTRICT未被Microsoft SQL Server 2012及更早的版本支持。

### NO ACTION

NO ACTION和RESTRICT非常像。但NO ACTION在嘗試修改主表後檢查引用完整性。如果在這種檢查之前執行了資料庫觸發器，可能在檢查時就不違背引用完整性。

### SET NULL, SET DEFAULT

從表中的外鍵列被分別設為NULL或預設值。

### Triggers

引用動作通常被實現為隱含的觸發器（即通常是隱藏的、具有系統生成名稱的觸發器）。因此，它們受到與使用者定義觸發器相同的限制，並且它們相對於其他觸發器的執行順序可能需要經過考慮；在某些情況下，可能需要將引用操作替換為其等效的使用者定義觸發器，以確保正確的執行順序，或解決變異表(mutating-table)的限制。

另一個重要限制出現在事務隔離時：對行的更改可能無法完全級聯，這是因為該行被事務無法「看到」的數據所引用，因此無法級聯。一個例子：當事務嘗試重新編號客戶帳戶時，同時另一事務正在嘗試為同一客戶創建新發票；雖然 CASCADE 規則可能會修改事務可以看到的所有發票行以使其與重新編號的客戶行保持一致，但它不會進入另一個事務來修改那裡的數據；因為資料庫不能保證兩個事務提交時數據一致，所以其中一個事務將被強制回滾（通常是先到先得）。

```sql
CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));

CREATE TRIGGER ins_sum BEFORE INSERT ON account
     FOR EACH ROW SET @sum = @sum + NEW.amount;
```

## 例子

一個簡單的關係：

```
 Supplier ('''SupplierNumber''', Name, Address)
 Invoice ('''InvoiceNumber''', Text, ''SupplierNumber'')
```

資料定義語言語句為：

```sql
CREATE TABLE Supplier (
  SupplierNumber INTEGER NOT NULL,
  Name           VARCHAR(20) NOT NULL,
  Address        VARCHAR(50) NOT NULL,
  CONSTRAINT supplier_pk PRIMARY KEY(SupplierNumber),
  CONSTRAINT number_value CHECK(SupplierNumber > 0)
)

CREATE TABLE Invoice (
  InvoiceNumber  INTEGER NOT NULL,
  Text           VARCHAR(4096),
  SupplierNumber INTEGER NOT NULL,
  CONSTRAINT invoice_pk PRIMARY KEY(InvoiceNumber),
  CONSTRAINT inumber_value CHECK (InvoiceNumber > 0),
  CONSTRAINT supplier_fk
    FOREIGN KEY(SupplierNumber) REFERENCES Supplier(SupplierNumber)
    ON UPDATE CASCADE ON DELETE RESTRICT
)
```

## 外部連結

- 一個詳細介紹外鍵的網站
- SQL-99 Foreign Keys
- PostgreSQL Foreign Keys
- MySQL Foreign Keys
- FirebirdSQL primary Keys
- SQLite support for Foreign Keys
- Microsoft SQL 2012 table_constraint (Transact-SQL)