Database/Postgresql

Deferrable과 Deferred에 대한 정리

bluebamus 2023. 12. 6.

 - 외래키 설정을 하는데 Deferrable과 Deferred의 설정에 대한 설명은 많은데 정작 어떤 동작을 하는지, 어떤 경우에 사용하면 좋은지에 대한 설명이 부족하여 정리하고자 한다.

 

 - 어떤 역할을 하는가?

   1) Deferrable: 이는 제약 조건이 연기 가능(deferred)하도록 설정하는 속성입니다. 기본적으로, 제약 조건은 'NOT DEFERRABLE'로 설정되어 있어, 트랜잭션 중 언제든지 즉시 검사됩니다. 하지만 'DEFERRABLE'로 설정된 제약 조건은, 필요에 따라 검사 시점을 연기할 수 있습니다.

 

   2)  Deferred: 이는 제약 조건이 초기에 연기(deferred)될지 아니면 즉시 검사될지를 결정하는 속성입니다. 'DEFERRABLE' 속성이 설정된 제약 조건에 대해, 'SET CONSTRAINTS' 명령을 사용하여 트랜잭션 중에 연기 상태를 변경할 수 있습니다.

 

   - 예를 들어, 'INITIALLY DEFERRED'로 설정된 'DEFERRABLE' 제약 조건은 트랜잭션 커밋 시점까지 검사가 연기됩니다. 반대로 'INITIALLY IMMEDIATE'로 설정된 경우에는, 각 명령문이 실행될 때마다 즉시 검사됩니다. 하지만 이 경우에도, 'SET CONSTRAINTS' 명령을 사용하여 트랜잭션 중에 제약 조건의 검사를 연기할 수 있습니다.


   - 따라서, 'Deferrable'과 'Deferred'는 특정 작업을 수행하는 동안 데이터의 일관성을 유지하기 위해 제약 조건의 검사 시점을 조절할 수 있는 유용한 기능입니다.

 

 - 즉시 검사와 커밋 시점 검사

 - PostgreSQL에서 제약 조건은 기본적으로 즉시 검사되지만, 'Deferred' 설정을 사용하여 트랜잭션 커밋 시점에 검사되도록 연기할 수 있습니다.

   1) 즉시 검사되는 경우 :

      - 트랜잭션 내의 각 SQL 문이 실행될 때 제약 조건을 검사합니다. 

         - 예시 1: '주문' 테이블에서 '고객ID' 필드가 '고객' 테이블의 'ID' 필드를 참조하는 외래 키라고 가정합시다. 새 주문을 추가할 때, 해당 고객 ID가 '고객' 테이블에 존재하지 않으면 외래 키 제약 조건이 즉시 위반되어 주문 추가가 실패합니다.

         - 예시 2: '사원' 테이블에 '이메일' 필드가 UNIQUE 제약 조건을 가지고 있다고 가정합시다. 새 사원을 추가하려고 할 때 동일한 이메일 주소가 이미 '사원' 테이블에 있으면 UNIQUE 제약 조건이 즉시 위반되어 새 사원 추가가 실패합니다.

 

   2) 'Deferred' 설정에 의해 검사 시점을 트랜잭션 커밋 시점에 검사되는 경우

      - 트랜잭션 내에서 일시적으로 제약 조건을 위반할 수 있게 합니다. 제약 조건의 검사는 트랜잭션 커밋 시점에 이루어집니다.
         - 예시 1: '직원' 테이블에 '직급' 필드가 '직급 순서' 테이블의 '직급' 필드를 참조하는 외래 키라고 가정합시다. 'Initially Deferred' 설정을 사용하면, 두 직원의 직급을 서로 교환하는 등의 작업을 수행할 수 있습니다. 직원 A의 직급을 먼저 변경하면 일시적으로 외래 키 제약 조건이 위반되지만, 트랜잭션 커밋 시점에는 직원 B의 직급도 변경되어 제약 조건이 만족됩니다.
         - 예시 2: 부모-자식 관계를 가지는 테이블에서 'Initially Deferred' 설정을 사용하면, 부모 행을 먼저 삭제하고 자식 행을 나중에 삭제하는 작업을 수행할 수 있습니다. 부모 행을 먼저 삭제하면 일시적으로 외래 키 제약 조건이 위반되지만, 트랜잭션 커밋 시점에는 자식 행도 삭제되어 제약 조건이 만족됩니다.

 

 - 실무에서 발생할 수 있는 사례

   1) 회사에서 직원들의 직급 순서를 관리하는 데이터베이스가 있다고 가정해봅시다. '직원' 테이블에는 각 직원의 '직급'이 있고, 이 직급은 '직급 순서' 테이블의 '직급'을 참조하는 외래 키입니다. '직급 순서' 테이블에는 모든 가능한 직급과 그들의 순서가 명시되어 있습니다.
   이제 두 직원의 직급을 바꾸려는 상황을 생각해보세요. 이 경우, 한 명의 직원의 직급을 변경하면 일시적으로 '직급 순서' 테이블에 없는 직급이 생길 수 있습니다. 이 때문에 외래 키 제약 조건이 위반되어, 작업이 실패하게 됩니다.
   이 문제를 해결하기 위해 'Deferrable'과 'Initially Deferred' 설정을 사용할 수 있습니다. 이 설정들을 사용하면, 트랜잭션 내에서 일시적으로 외래 키 제약 조건을 위반할 수 있습니다. 즉, 먼저 한 직원의 직급을 변경하고, 그 다음 다른 직원의 직급을 변경할 수 있습니다. 이렇게 하면, 트랜잭션의 끝까지 외래 키 제약 조건의 검사를 연기할 수 있으므로, 두 직원의 직급을 성공적으로 교환할 수 있습니다.

 

   2)  계층적 데이터 조작: 부모-자식 관계를 가지는 테이블에서 자식 행을 먼저 업데이트하고, 그 후에 부모 행을 업데이트해야 하는 상황을 생각해보세요. 이 경우에도 외래 키 제약 조건이 위반되어 업데이트가 실패하게 됩니다. 하지만 'Deferrable' 설정을 사용하면, 부모 행의 업데이트를 트랜잭션의 끝까지 연기할 수 있습니다.

 

   3) 일시적인 데이터 비일관성: '주문' 테이블과 '고객' 테이블이 있고, '주문' 테이블이 '고객' 테이블을 참조하는 외래 키를 가지고 있다고 가정해봅시다. 새로운 고객과 그의 주문을 동시에 등록하려고 할 때, '고객' 테이블에 데이터가 먼저 등록되지 않으면 외래 키 제약 조건이 위반되어 '주문' 테이블에 데이터를 등록할 수 없습니다. 하지만 'Deferrable' 설정을 사용하면, '고객' 테이블의 데이터 등록을 트랜잭션의 끝까지 연기할 수 있습니다.

댓글