상상해봐, 너가 데이터베이스를 만들었는데, 거기엔 고객(customers) 테이블이랑 그 고객과 연결된 주문(orders) 테이블이 있어. 근데 어느 순간 이런 고민이 생길 수 있지: 만약에 customers에서 고객을 삭제하면 어떻게 해야 할까? 그 고객의 주문도 같이 삭제해야 할까, 아니면 주문이 "고아"처럼 남아서 존재하지 않는 고객을 참조하게 둘까? 그리고 만약에 고객의 ID를 바꾸고 싶으면? 바로 이런 상황에서 cascade 연산(CASCADE)이랑 제약조건(RESTRICT)이 데이터베이스 동작을 제어하는 데 등장하는 거야.
ON DELETE CASCADE는 부모 테이블에서 레코드를 삭제하면 연결된 레코드도 자동으로 삭제해주는 메커니즘이야. 즉, 고객을 삭제하면 그 고객과 연결된 모든 주문도 같이 삭제돼.
이건 이렇게 동작해. 외래키 정의에 ON DELETE CASCADE를 추가하면, 데이터베이스가 "아, 연결된 레코드도 같이 지워야겠구나"라고 알아서 처리해줘.
예시
예를 들어, customers랑 orders 두 테이블이 있다고 해보자. 고객(customers)은 여러 개의 주문(orders)을 가질 수 있고, 이건 ONE-TO-MANY 관계야. 우리는 고객을 삭제하면 그 고객의 주문도 다 같이 삭제되길 원해.
-- 고객 테이블 만들기
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
-- 고객 테이블을 참조하는 외래키가 있는 주문 테이블 만들기
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE,
order_date DATE NOT NULL
);
테이블에 데이터 넣기
-- 고객 테이블에 데이터 넣기
INSERT INTO customers (name) VALUES ('이반'), ('안나');
-- 주문 테이블에 데이터 넣기
INSERT INTO orders (customer_id, order_date) VALUES
(1, '2023-10-01'),
(1, '2023-10-02'),
(2, '2023-10-03');
orders 테이블:
| order_id | customer_id | order_date |
|---|---|---|
| 1 | 1 | 2023-10-01 |
| 2 | 1 | 2023-10-02 |
| 3 | 2 | 2023-10-03 |
고객 삭제하고 결과 확인하기
-- ID가 1인 고객 삭제
DELETE FROM customers WHERE customer_id = 1;
-- 주문 테이블에 뭐가 남았는지 확인
SELECT * FROM orders;
| order_id | customer_id | order_date |
|---|---|---|
| 3 | 2 | 2023-10-03 |
보다시피, 삭제된 고객과 연결된 주문도 같이 삭제됐어.
변경 제한: ON UPDATE RESTRICT
ON UPDATE RESTRICT는 부모 테이블의 값을 자식 테이블이 참조하고 있을 때, 그 값을 바꾸지 못하게 막아주는 "보호막" 같은 거야. 데이터 무결성을 깨뜨릴 수 있는 변경을 막아주는 거지.
어떻게 동작하냐면, ON UPDATE RESTRICT를 외래키에 추가하면, 부모 테이블의 키를 자식 테이블이 참조하고 있으면 그 키를 바꿀 수 없어.
예시
아까 그 customers랑 orders 테이블을 그대로 쓰는데, 이번엔 외래키에 변경 제한을 추가해볼게.
-- 주문 테이블을 변경 제한 옵션으로 다시 만들기
DROP TABLE orders;
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON UPDATE RESTRICT,
order_date DATE NOT NULL
);
고객 ID를 바꿔보자
-- 고객 ID 2를 5로 바꿔보려고 시도
UPDATE customers
SET customer_id = 5
WHERE customer_id = 2;
결과:
ERROR: update or delete on table "customers" violates foreign key constraint
DETAIL: Key (customer_id)=(2) is still referenced from table "orders".
보다시피, 데이터베이스가 에러를 던졌어. 키를 바꾸면 테이블 간의 연결이 깨질 수 있으니까 막아준 거지.
UPDATE랑 그 동작의 디테일은 다음 레벨에서 더 자세히 알려줄게 :P
ON DELETE CASCADE랑 ON UPDATE RESTRICT 같이 쓰기
당연히, cascade 연산(CASCADE)이랑 제약조건(RESTRICT)은 같이 쓸 수 있어. 예를 들어, 부모 레코드를 삭제할 때 연결된 데이터를 자동으로 삭제(ON DELETE CASCADE)하면서, 그 ID는 바꿀 수 없게(ON UPDATE RESTRICT) 막을 수도 있지. 이렇게 하면 원치 않는 결과를 막을 수 있어.
예시
이번엔 두 옵션을 다 써서 주문 테이블을 다시 만들어보자:
DROP TABLE orders;
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id)
ON DELETE CASCADE
ON UPDATE RESTRICT,
order_date DATE NOT NULL
);
이제:
- 고객을 삭제하면 그 고객의 주문도 다 같이 삭제돼.
- 고객의 ID를 바꾸려고 하면 에러가 나와.
실제 프로젝트에서 왜 중요할까?
CASCADE랑 RESTRICT는 테이블이 많고 복잡한 시스템에서 진짜 중요해. 예를 들어:
온라인 쇼핑몰에서 고객이 주문을 할 수 있잖아. 만약 고객이 자기 프로필을 삭제하면, 더 이상 참조되지 않는 주문이 데이터베이스에 남아있으면 안 되겠지? 이럴 때 ON DELETE CASCADE가 딱이야.
반대로, 유니크 키를 실수로 바꿔서 테이블 간의 연결이 깨지는 걸 막고 싶을 때는 ON UPDATE RESTRICT가 필요해.
GO TO FULL VERSION