외래 키

**외래 키**(**외부 키**, foreign key)는 한 테이블의 속성 셋으로, 다른 테이블의 기본 키를 참조하여 두 테이블을 연결하는 역할을 한다. 관계형 데이터베이스의 맥락에서 외래 키는 참조 무결성 제약 조건의 대상이 되는데, 즉 한 관계 R에 있는 외래 키 속성들로 구성된 튜플은 다른 (반드시 별개일 필요는 없는) 관계 S에도 존재해야 하며, 더 나아가 해당 속성들은 S에서 후보 키여야 한다.

다시 말해, 외래 키는 후보 키를 참조하는 속성들의 집합이다. 예를 들어, TEAM이라는 테이블에 MEMBER_NAME이라는 속성이 있고, 이것이 PERSON 테이블의 후보 키인 PERSON_NAME을 참조하는 외래 키일 수 있다. MEMBER_NAME이 외래 키이므로, TEAM의 멤버 이름으로 존재하는 모든 값은 PERSON 테이블의 사람 이름으로도 존재해야 한다. 즉, TEAM의 모든 멤버는 동시에 PERSON이기도 하다.

## 개요
외래 키를 포함하는 테이블을 자식 테이블(child table)이라 부르고, 후보 키를 포함하는 테이블을 참조되는 테이블 또는 부모 테이블(parent table)이라고 한다. 데이터베이스 관계형 모델링 및 구현에서 후보 키는 하나 이상의 속성 집합으로, 그 값은 관계 내의 각 튜플(행)에 대해 고유함이 보장된다. 어떤 튜플에 대한 후보 키 속성 값 또는 값의 조합은 해당 관계 내의 다른 어떤 튜플에서도 중복될 수 없다.

외래 키의 목적은 참조되는 테이블의 특정 행을 식별하는 것이므로, 일반적으로 외래 키는 부모 테이블의 어떤 행에 있는 후보 키와 동일하거나, 혹은 아예 값이 없어야(NULL 값) 한다. 이 규칙을 두 테이블 사이의 참조 무결성 제약 조건이라고 한다.
이러한 제약 조건을 위반하면 많은 데이터베이스 문제의 원인이 될 수 있기 때문에, 대부분의 데이터베이스 관리 시스템은 null이 아닌 모든 외래 키가 참조되는 테이블의 행과 일치하도록 보장하는 메커니즘을 제공한다.

예를 들어, 모든 고객 데이터를 포함하는 CUSTOMER 테이블과 모든 고객 주문을 포함하는 ORDER 테이블이 있는 데이터베이스를 가정해 보자. 비즈니스 요구사항상 각 주문은 반드시 단 한 명의 고객을 참조해야 한다고 가정한다. 이를 데이터베이스에 반영하기 위해 ORDER 테이블에 외래 키 컬럼(예: CUSTOMERID)을 추가하고, 이것이 CUSTOMER의 기본 키(예: ID)를 참조하게 한다. 테이블의 기본 키는 고유해야 하고 CUSTOMERID는 그 기본 키 필드의 값만 가질 수 있으므로, CUSTOMERID에 값이 있을 때 그것이 주문을 한 특정 고객을 식별할 것이라고 간주할 수 있다. 그러나 CUSTOMER 테이블의 행이 삭제되거나 ID 컬럼이 변경될 때 ORDER 테이블이 최신 상태로 유지되지 않는다면 더 이상 이를 보장할 수 없으며, 이러한 테이블들을 다루는 작업은 더 어려워질 수 있다. 실제 세계의 많은 데이터베이스는 마스터 테이블의 외래 키를 물리적으로 삭제하는 대신 '비활성화'하거나, 변경이 필요할 때 외래 키에 대한 모든 참조를 수정하는 복잡한 업데이트 프로그램을 통해 이 문제를 해결한다.

외래 키는 데이터베이스 설계에서 필수적인 역할을 한다. 데이터베이스 설계의 중요한 부분 중 하나는 현실 세계의 엔티티 간의 관계가 외래 키를 사용하여 한 테이블에서 다른 테이블을 참조함으로써 데이터베이스에 반영되도록 하는 것이다.
데이터베이스 설계의 또 다른 중요한 부분은 데이터베이스 정규화로, 여기서 테이블은 분해되며 외래 키를 통해 다시 재구성될 수 있게 된다.

참조하는(또는 자식) 테이블의 여러 행이 참조되는(또는 부모) 테이블의 동일한 행을 참조할 수 있다. 이 경우 두 테이블 사이의 관계는 참조하는 테이블과 참조되는 테이블 간의 일대다 관계라고 불린다.

또한, 자식 테이블과 부모 테이블은 사실 동일한 테이블일 수 있다. 즉, 외래 키가 동일한 테이블을 다시 참조하는 경우이다. 이러한 외래 키는 에서 자기 참조(self-referencing) 또는 재귀적 외래 키로 알려져 있다. 데이터베이스 관리 시스템에서 이는 종종 동일한 테이블에 대한 첫 번째 참조와 두 번째 참조를 연결함으로써 달성된다.

테이블은 여러 개의 외래 키를 가질 수 있으며, 각 외래 키는 서로 다른 부모 테이블을 가질 수 있다. 각 외래 키는 데이터베이스 시스템에 의해 독립적으로 강제된다. 따라서 외래 키를 사용하여 테이블 간의 연쇄적인 관계를 설정할 수 있다.

외래 키는 한 관계 내의 속성 또는 속성 세트로 정의되며, 그 값은 다른 관계의 기본 키와 일치한다. 기존 테이블에 이러한 제약 조건을 추가하는 구문은 에 다음과 같이 정의되어 있다. `REFERENCES` 절에서 컬럼 목록을 생략하면 외래 키가 참조되는 테이블의 기본 키를 참조한다는 것을 의미한다.
마찬가지로, 외래 키는 `CREATE TABLE` SQL 문의 일부로 정의될 수 있다.


```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
```


* **child_table**: 정의될 외래 키를 포함하는 테이블 또는 뷰의 이름.
* **parent_table**: 외래 키가 적용되는 기본 키를 가진 테이블 또는 뷰의 이름. 기본 키는 이미 정의되어 있어야 한다.
* **col3** 및 **col4**: 외래 키를 구성하는 컬럼의 이름. 외래 키는 최소 1개, 최대 8개의 컬럼을 가져야 한다.

## 참조 동작
데이터베이스 관리 시스템은 참조 제약 조건을 강제하기 때문에, 참조되는 테이블의 행이 삭제(또는 업데이트)될 경우 데이터 무결성을 보장해야 한다. 참조하는 테이블에 종속된 행들이 여전히 존재한다면, 해당 참조들을 고려해야 한다. 은 이러한 상황에서 발생해야 할 5가지 서로 다른 **참조 동작**(referential actions)을 규정한다.
* CASCADE
* RESTRICT
* NO ACTION
* SET NULL
* SET DEFAULT

### CASCADE
부모(참조되는) 테이블의 행이 삭제(또는 업데이트)될 때마다, 일치하는 외래 키 컬럼을 가진 자식(참조하는) 테이블의 해당 행들도 함께 삭제(또는 업데이트)된다. 이를 연쇄 삭제(또는 업데이트)라고 한다.

### RESTRICT
참조되는 테이블의 값을 참조하는 자식 테이블의 행이 존재하는 경우, 해당 값을 업데이트하거나 삭제할 수 없다.

마찬가지로, 참조하는 자식 테이블에서 해당 행에 대한 참조가 있는 한 행을 삭제할 수 없다.

RESTRICT(및 CASCADE)를 더 잘 이해하기 위해, 즉각적으로 명확하지 않을 수 있는 다음 차이점을 주목하는 것이 도움이 된다. 참조 동작 CASCADE는 CASCADE라는 단어가 사용된 (자식) 테이블 자체의 "동작"을 수정한다. 예를 들어, ON DELETE CASCADE는 사실상 "참조된 행이 다른 테이블(마스터 테이블)에서 삭제되면, 나에게서도 삭제하라"고 말하는 것이다. 반면, 참조 동작 RESTRICT는 RESTRICT라는 단어가 마스터 테이블이 아닌 자식 테이블에 나타남에도 불구하고 마스터 테이블의 "동작"을 수정한다! 따라서 ON DELETE RESTRICT는 사실상 "누군가가 다른 테이블(마스터 테이블)에서 행을 삭제하려고 할 때, 그 테이블에서의 삭제를 방지하라(물론 나에게서도 삭제하지 말아야 하지만, 그것이 여기서의 핵심은 아니다)"고 말하는 것이다.

RESTRICT는 Microsoft SQL 2012 이전 버전에서는 지원되지 않는다.

### NO ACTION
NO ACTION과 RESTRICT는 매우 유사하다. NO ACTION과 RESTRICT의 주요 차이점은 NO ACTION의 경우 테이블을 수정하려고 시도한 '후에' 참조 무결성 검사를 수행한다는 것이다. RESTRICT는 UPDATE 또는 DELETE 문을 실행하기 '전에' 검사를 수행한다. 참조 무결성 검사가 실패할 경우 두 참조 동작은 동일하게 작동한다. 즉, UPDATE 또는 DELETE 문은 오류를 발생시킨다.

다시 말해, 참조 동작 NO ACTION을 사용하여 참조되는 테이블에서 UPDATE 또는 DELETE 문이 실행되면, DBMS는 문 실행이 끝날 때 참조 관계가 위반되지 않았는지 확인한다. 이는 작업이 제약 조건을 위반할 것이라고 처음에 가정하는 RESTRICT와 다르다. NO ACTION을 사용하면, 트리거나 문 자체의 의미론적 결과로 인해 제약 조건이 최종적으로 확인되는 시점에 외래 키 관계가 위반되지 않는 상태가 될 수 있으며, 이 경우 문이 성공적으로 완료될 수 있다.

### SET NULL, SET DEFAULT
일반적으로 SET NULL 또는 SET DEFAULT에 대해 DBMS가 취하는 조치는 ON DELETE와 ON UPDATE 모두에서 동일하다. 영향받는 참조 속성의 값은 SET NULL의 경우 NULL로 변경되고, SET DEFAULT의 경우 지정된 기본값으로 변경된다.

### 트리거
참조 동작은 일반적으로 암시적 데이터베이스 트리거 (즉, 시스템에서 생성된 이름을 가진, 종종 숨겨진 트리거)로 구현된다. 따라서 사용자 정의 트리거와 동일한 제한 사항이 적용되며, 다른 트리거와의 실행 순서를 고려해야 할 수도 있다. 경우에 따라 적절한 실행 순서를 보장하거나 테이블 변이(mutating-table) 제한을 우회하기 위해 참조 동작을 그에 상응하는 사용자 정의 트리거로 교체해야 할 수도 있다.

또 다른 중요한 제한 사항은 트랜잭션 격리에서 나타난다. 트랜잭션이 "볼 수 없는" 데이터가 해당 행을 참조하고 있을 수 있기 때문에, 행에 대한 변경 사항이 완전히 연쇄(cascade)되지 못할 수 있다. 예를 들어, 당신의 트랜잭션이 고객 계정 번호를 변경하려고 시도하는 동안, 동시에 다른 트랜잭션이 동일한 고객에 대해 새로운 송장을 생성하려고 시도하는 경우이다. 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;
```


## 예시
외래 키를 설명하기 위한 첫 번째 예로, 송장(invoice) 테이블이 있고 각 송장이 특정 공급업체(supplier)와 연결된 계정 데이터베이스를 가정해 보자. 공급업체의 상세 정보(이름 및 주소 등)는 별도의 테이블에 보관되며, 각 공급업체에는 이를 식별하기 위한 '공급업체 번호'가 부여된다. 각 송장 레코드는 해당 송장에 대한 공급업체 번호를 포함하는 속성을 갖는다. 이때 '공급업체 번호'는 Supplier 테이블의 기본 키가 된다. Invoice 테이블의 외래 키는 해당 기본 키를 가리킨다. 관계형 스키마는 다음과 같다. 기본 키는 굵게 표시하고, 외래 키는 이탤릭체로 표시한다.

  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
분류:데이터베이스 관리 시스템
분류:데이터 모델링