관계형 데이터베이스 설계는 테이블 구조 뿐 아니라 테이블간의 관계도 구성해야 한다. 여기서 참조 무결성(Reference Integrity)은 데이터베이스를 적절히 설계하고 운영하는 데 있어 중요한 부분이다.
일반적으로 사람들이 FK를 무시하라고 하는 이유
FK 제약 조건을 사용하지 않는 경우 참조 무결성을 보장하는 다른 방법을 강구해야 한다.
FK를 사용하지 않는 경우 application에서 관련 작업을 진행해 줘야 한다.
즉,
비즈니스 로직 작성 시 실수를 해서는 안된다.
Bugs 테이블에 새로운 행을 삽입한다고 가정하고 FK를 지키기 위한 작업을 진행하면 다음과 같을 것이다.
버그 테이블에 행을 삭제하는 경우에는 다음과 같다.
하지만, 1번 작업과 2번 작업 사이에 다른 사용자에 의해 데이터 변경이 발생하면???
해결 방법이 없다. 즉, 참조 무결성이 깨질 수 있다.
유일한 해결책은 확인전에 Bugs 테이블을 잠그고 작업을 진행하는 것이다. 이건 동시성과 확장성이 떨어지게 된다.
유효하지 않은 상태의 bugs 데이터를 찾기위해 다음과 같은 sql을 사용할 수도 있다.
SELECT b.bug_id, b.status
FROM Bugs b LEFT OUTER JOIN BugStatus s
ON (b.status = s.status)
WHERE s.status IS NULL;
application단에서 오류 확인을 하려면 위와 같은 쿼리를 모든 참조 관계에서 처리해야 하는데, 이것을 주기적으로 계속적으로 한다는 것은 굉장히 불필요한 일일 것이다.
필요에 따라서는 이상한 값을 가진 레코드를 default값으로 변경할수도 있지만, 이것도 때에 따라서는 어떤 값으로 변경할지 결정할 수 없는 것도 있을 것이고, 이러한 작업 자체가 참으로 귀찮은 일이다.
데이터베이스를 변경하는 모든 로직은 여러 함수를 사용하여 비즈니스 로직도 다양하다. 이 모든 코드에서 모든 경우에 대해 문제가 없도록 변경을 적용하는 것을 어떻게 확신할 수 있을 것인지도 생각해 봐야 한다.
데이터베이스는 일관성 있게 유지해야 하는데, application과 스크립트가 데이터베이스의 일관성 있게 작성되었는지 일일히 확인하는 것은 어려운 일이다.
많은 개발자들은 여러 테이블과 관련된 칼럼을 변경할 때 불편하기 때문에 FK 제약 조건을 꺼린다.
FK제약 조건을 위반하지 않기 위해 자식 행을 먼저 삭제해야 한다.
[예제]
다음과 같이 진행될 것이다.
삭제시는 다음과 같다.
DELETE FROM BugStatus WHERE status = 'BOGUS' ; --에러
DELETE FROM Bugs WHERE status = 'BOGUS' ;
DELETE FROM BugStatus WHERE status = 'BOGUS' ; --재시도 성공
변경시는 다음과 같다.
UPDATE BugStatus SET status = 'INVALID' WHERE status = 'BOGUS'; --에러
UPDATE Bugs SET status = 'INVALID' WHERE status = 'BOGUS'; --에러
위와 같은 문제로 FK를 사용하려고 하지 않지만, 간단하고 효율적인 방법으로 철히할 수 있다.
키가 없는 안티패턴을 사용하고 있는 경우 다음과 같은 고민을 하게 된다.
다음과 같은 경우는 안티패턴 사용이 합당할 수 있다.
[오류검증]
오류를 방지하거나 바로잡거나 또는 발생하는 즉시 관심을 가져 제품의 결함을 제거하는 데 도움이 되는 생산 공적을 뜻함.
품질을 향상시키고 정정 요구를 감소시켜, 추가비용을 상쇄하고도 남는다.
FK는 추가적으로 다음과 같은 기능이 존재한다.
[CASCADING UPDATE]
이 기능을 사용하면 부모 행을 update, delete할 때 해당 부모를 참조하는 자식 행을 알아서 처리해준다.
FK가 약간의 오버헤드가 있지만, 다음과 같은 효과가 있기 때문에 효율적이다.