여러 개의 부모테이블로부터 하나의 자식테이블을 공유할 수 있다.
이에 대해 오류가 발생할 상황이 만들어진다.
CREATE TABLE Comment (
Comment_id number PRIMARY KEY,
Bug_id number NOT NULL,
author_id number NOT NULL,
comment_date date NOT NULL,
comment varchar2(4000) number NOT NULL
FOREIGN KEY (issue_id)
REFFERNCES Bugs(issue_id) OR FeatureRequests(issue_id)
);
SELECT c.*, i.summary, i.status
FROM Comments as c
JOIN c.issue_type AS I USING (isuue_id);
Comments테이블은 Bugs테이블의 행이나 FeatureRequests테이블의 행 중 하나와만 대응한다.
이를 ERD로 표기하면 아래와 같다. (Exclusive 관계)
이중 목적의 FK사용에 대한 해법을 일컬어 다형성 연관(Polymorphic Associations) 또는 난잡한 연관(Promiscuous Association)이라고 한다.
다형성 연관을 작동하게 하려면 FK컬럼인 issue_id컬럼에 들어가는 값에 대응하여 참조하는 테이블정보를 추가적으로 관리하여야 한다.
CREATE TABLE Comment (
Comment_id number PRIMARY KEY,
Issue_type VARCHAR2(20), -- bugs 또는 featureRequests 입력
Issue_id number NOT NULL,
Bug_id number NOT NULL,
author_id number NOT NULL,
comment_date date NOT NULL,
comment varchar2(4000) number NOT NULL
);
FK는 하나의 테이블만 참조할 수 있기 때문에 다형성 연관을 사용할 경우에는 이 연관을 메타데이터에 선언할 수 없기 때문에 FK선언을 할 수 없다.
그 결과로 다음과 같은 단점이 드러나게 된다.
예제에서 사용되는 Comments테이블의 issue_id값은 Bugs테이블이나 FeatureRequests테이블 중 한 쪽에만 있을 수도 있고 양쪽 모두 있을 수도 있고 양쪽 모두 없을 수도 있다.
SELECT *
FROM Bugs AS b JOIN Comments AS c
ON (b.issue_id = c.issue_id AND c.issue_type = 'Bugs')
WHERE b.issue_id = 1234;
위와 같이 되어 있다면 Bugs테이블의 내용은 잘 동작한다.
그러나 FeatureRequests테이블의 내용은 나오지 않는다. SQL수행 시 관련된 테이블과의 관계를 다음처럼 명시해야 한다.
SELECT *
FROM Comments AS c
LEFT OUTER JOIN Bugs AS b
ON (b.issue_id = c.issue_id AND c.issue_type = 'Bugs')
LEFT OUTER JOIN FeatureRequests AS f
ON (f.issue_id = c.issue_id AND c.issue_type = 'FeatureRequests')
WHERE b.issue_id = 1234;
부모테이블에 따라 구분되어 사용되는 값이 많아진다면 점점 컬럼이 2개씩(부모가 2개라면..)로 증가하게 되고 복잡하게 될 것이다.
테이블 설계 시 아래와 같은 말이 들린다면 안티패턴인지 식별할 필요가 있다.
합당한 경우가 없다. 단 특정 객체관계 프로그래밍 프레임워크를 사용하는 경우 이 안티패턴사용이 불가피한 경우가 있다. 이 경우 애플리케이션 로직을 캡슐화하여 참조정합성 유지에 도움을 줄 수 있다.
다형성 연관의 단점을 피하면서 필요한 데이터 모델을 지원하기 위해서는 데이터베이스를 다시 설계하는 것이 낫다.
자식테이블 Comments는 여러 부모테이블을 참조할 수 없으므로 Comments테이블과 각 부모테이블과의 교차 테이블을 생성하여 각 부모테이블의 FK뿐아니라 Comments테이블의 FK도 포함 시킨다.
CREATE TABLE BugsComment (
Issue_id number NOT NULL,
Comment_id number NOT NULL,
PRIMARY KEY (Issue_id, Comment_id)
FOREIGN KEY (issue_id) REFERENCES Bugs(issue_id),
FOREIGN KEY (Comment_id) REFERENCES Comments(Comment_id),
);
CREATE TABLE FeaturesComment (
Issue_id number NOT NULL,
Comment_id number NOT NULL,
PRIMARY KEY (Issue_id, Comment_id)
FOREIGN KEY (issue_id) REFERENCES FeatureRequests(issue_id),
FOREIGN KEY (Comment_id) REFERENCES Comments(Comment_id)
);
하나의 덧글이 한 번만 참조됨을 보장받아야 하는 경우 다음처럼 UNIQUE키를 설정할 수 있다.
CREATE TABLE BugsComment (
Issue_id number NOT NULL,
Comment_id number NOT NULL,
UNIQUE KEY (comment_id),
PRIMARY KEY (Issue_id, Comment_id)
FOREIGN KEY (issue_id) REFERENCES Bugs(issue_id),
FOREIGN KEY (Comment_id) REFERENCES Comments(Comment_id),
);
Issue와 같은 조상 테이블을 사용한다면 FK를 통해 데이터베이스 정합성을 강제할 수 있다.