본문으로 바로가기 본문 바로가기
WIKI 기술 지식 베이스

외래 키 정의하기 (Define foreign keys)

원문 보기 위키 갱신

D1에서 테이블 간의 관계를 강제하는 외래 키 제약 조건(foreign key constraints)에 대해 다루는 문서예요. 데이터 무결성을 지키고 참조 무결성을 유지하는 방법을 알려줘요.

출처: 문서

본문

Define foreign keys

외래 키 제약 조건을 사용하면 테이블 간의 관계를 강제할 수 있어요. 예를 들어 외래 키로 users 테이블의 user_id와 orders 테이블의 user_id 사이에 엄격한 연결을 만들어, 존재하지 않는 사용자에 대한 주문을 만들 수 없게 할 수 있어요.

외래 키 제약 조건은 다른 테이블의 행을 참조하는 행을 삭제하지 못하게 방지할 수도 있어요. 예를 들어 orders 테이블의 행이 참조하는 users 테이블의 행을 삭제하지 못하게 막을 수 있어요.

기본적으로 D1은 모든 쿼리와 마이그레이션 내에서 외래 키 제약 조건이 유효하도록 강제해요. 이는 SQLite에서 모든 트랜잭션에 PRAGMA foreign_keys = on을 설정했을 때 관찰할 수 있는 동작과 동일해요.

외래 키 제약 조건 지연시키기 (Defer foreign key constraints)

D1 데이터베이스에 대해 쿼리, 마이그레이션 또는 데이터 가져오기를 실행할 때, 테이블 생성이나 스키마 변경 중에 외래 키 검증을 비활성화해야 하는 상황이 있을 수 있어요.

D1의 외래 키 강제는 SQLite의 PRAGMA foreign_keys = on 지시문과 동일해요. D1은 모든 쿼리를 암시적 트랜잭션 안에서 실행하므로, 사용자 쿼리는 쿼리나 마이그레이션 중에 이를 변경할 수 없어요.

대신 D1은 PRAGMA defer_foreign_keys = on 또는 off를 호출하게 해줘요. 이를 통해 외래 키 제약 조건을 일시적으로(현재 트랜잭션이 끝날 때까지) 위반할 수 있어요.

PRAGMA defer_foreign_keys = off를 호출한다고 해서 현재 트랜잭션 밖의 외래 키 강제가 비활성화되지는 않아요. 트랜잭션이 끝날 때 해결되지 않은 외래 키 위반이 있다면 FOREIGN KEY constraint failed 오류와 함께 실패해요.

외래 키 강제를 지연시키려면 트랜잭션 시작 시 또는 제약 조건을 위반할 변경 앞에 PRAGMA defer_foreign_keys = on을 설정하세요.

-- Defer foreign key enforcement in this transaction.
PRAGMA defer_foreign_keys = on

-- Run your CREATE TABLE or ALTER TABLE / COLUMN statements
ALTER TABLE users ...

-- This is implicit if not set by the end of the transaction.
PRAGMA defer_foreign_keys = off

해결되지 않은 외래 키 제약 조건을 해결한 직후에 PRAGMA defer_foreign_keys = off를 명시적으로 설정할 수도 있어요. 아직 외래 키 제약 조건이 남아 있다면 FOREIGN KEY constraint failed 오류를 받고 위반을 해결해야 해요.

외래 키 관계 정의하기 (Define a foreign key relationship)

외래 키 관계는 CREATE TABLE로 테이블을 만들 때 정의하거나 ALTER TABLE 문으로 기존 테이블에 컬럼을 추가할 때 정의할 수 있어요.

두 개의 테이블을 가진 전자상거래 웹사이트를 예로 들어 설명할게요.

  • users 테이블: 사용자 계정의 공통 속성을 정의하며, 고유한 user_id 식별자를 포함해요.
  • orders 테이블: 주문을 사용자 테이블의 user_id에 매핑해요.

이 매핑은 FOREIGN KEY로 정의되며 다음을 보장해요.

  • 외래 키 제약 조건을 위반하는 users 테이블의 행은 삭제할 수 없어요. 즉, 유효한 사용자로 매핑할 수 없는 주문이 생기지 않게 돼요.
  • orders는 항상 유효한 user_id에 대해 정의되어, 유효하지 않은(또는 존재하지 않는) 사용자를 참조하는 주문이 생기는 위험을 줄여줘요.
CREATE TABLE users (
    user_id INTEGER PRIMARY KEY,
    email_address TEXT,
    name TEXT,
    metadata TEXT
)

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    status INTEGER,
    item_desc TEXT,
    shipped_date INTEGER,
    user_who_ordered INTEGER,
    FOREIGN KEY(user_who_ordered) REFERENCES users(user_id)
)

테이블당 여러 외래 키 관계를 정의할 수 있고, 외래 키 정의는 전체 데이터베이스 스키마 안에서 여러 테이블을 참조할 수 있어요.

외래 키 액션 (Foreign key actions)

외래 키 정의의 일부로 액션을 정의해 상위 행(REFERENCES table(column))에 대한 변경을 제한하거나 전파할 수 있어요. 액션을 정의하면 애플리케이션에서 외래 키 제약 조건을 사용하는 것을 더 쉽게 이해할 수 있고, 관련 데이터를 정리하거나 데이터가 고립(islanded)되는 것을 방지하는 데 도움이 돼요.

외래 키 관계의 일부로 ON UPDATE 및/또는 ON DELETE 절을 정의할 때 설정할 수 있는 액션은 다섯 가지가 있어요. 요구 사항에 따라 ON UPDATE와 ON DELETE에 서로 다른 액션을 정의할 수도 있어요.

  • CASCADE - 상위 키를 업데이트하거나 삭제하면 연결된 모든 하위 키(행)가 삭제돼요.
  • RESTRICT - 어떤 하위 키라도 상위 키를 참조하면 상위 키는 업데이트하거나 삭제할 수 없어요. 기본 외래 키 강제와 달리 RESTRICT가 적용된 관계는 트랜잭션이 끝날 때가 아니라 즉시 오류를 반환해요.
  • SET DEFAULT - 외래 키 정의가 참조하는 하위 컬럼들을 스키마에 정의된 DEFAULT 값으로 설정해요. 하위 컬럼에 DEFAULT가 설정되어 있지 않다면 이 액션을 사용할 수 없어요.
  • SET NULL - 외래 키 정의가 참조하는 하위 컬럼들을 SQL NULL로 설정해요.
  • NO ACTION - 아무 작업도 하지 않아요.

다음 예시에서 ON DELETE CASCADE를 정의했으므로 users 테이블에서 사용자를 삭제하면 scores 테이블의 관련 행이 모두 삭제돼요. 완전히 삭제한 사용자에 대한 점수를 유지하고 싶지 않다면 scores 테이블의 관련 행을 모두 삭제하세요. 다만 이는 다른 사용자들이 여전히 유효했던 점수를 더 이상 조회하거나 참조할 수 없게 될 수도 있음을 의미해요.

CREATE TABLE users (
    user_id INTEGER PRIMARY KEY,
    email_address TEXT,
)

CREATE TABLE scores (
    score_id INTEGER PRIMARY KEY,
    game TEXT,
    score INTEGER,
    player_id INTEGER,
    FOREIGN KEY(player_id) REFERENCES users(user_id) ON DELETE CASCADE
)

다음 단계 (Next Steps)

더 알아보기 (Learn more)