프로젝트들을 진행하면서 항상 DB 설계에 대한 고민이 많았습니다. 특히 여러 개의 외부키를 도입하고 조금만 관계가 복잡해지면 쿼리와 비지니스 로직이 순식간에 스파게티가 되다 보니까, SQL을 언젠가 공부하긴 해야겠다고 생각해 왔습니다. 그러던 중에 빌 카윈(Bill Karwin)의 <SQL Antipatterns ― Avoiding the Pitfalls of Database Programming>을 학교 도서관에서 우연히 발견했습니다. 복잡한 코드나 이론이 있는 것은 아니지만, 좋은 DB의 설계와 관리를 위한 최소한의 상식을 배울 수 있었습니다. 또 한편으로는, 안티패턴을 제시하고 그에 대응하는 좋은 디자인 패턴을 연구하는 책의 형식도 무척이나 인상적이었습니다. 이에 감명 받아서, SQL에서의 (안티)패턴을 정리하는 이 문서를 작성하게 되었습니다. 이하 대부분의 항목은 사실 제가 갖고 있던 나쁜 SQL 사용 습관이기도 합니다. 1)
ANSI SQL 표준에서 벗어나는 방법으로 자료 사이의 관계를 표현하려고 하면 대체로 안티패턴에 해당합니다. 자료 사이의 관계를 직렬화(serialize) 해서 값으로 저장하거나, 연관의 정보를 메타데이터가 아닌 값으로 저장하는 등의 설계가 모두 안티패턴입니다. 만약 표현하고자 하는 관계를 정말 SQL로 나타낼 수 없다면, SQL 혹은 RDBMS 자체가 부적절한 선택일 수 있습니다.
비슷비슷한 테이블들이 공통된 칼럼을 갖는 경우가 있습니다. 예를 들어서 위키의 게시물과 파일은 공통적으로 제목, 작성 시각, 접근 권한 등등을 가지고 있을 수 있죠. 게다가 같은 연관(relation)을 갖고 있을 수도 있습니다. 예를 들어서 위키의 문서와 파일은 모두 작성자와의 연관이나 다른 문서와의 역링크 연관을 가집니다. 이때 단순히 여러 테이블과 하나의 테이블 사이에 연관을 생성하는 것은 안티패턴에 해당합니다. 지금까지의 예시로 보자면, 다음과 같이 문서 테이블과 파일 테이블이 모두 같은 댓글 테이블에 연관되는 상황입니다.
+-----------+ | Documents | * * * * * +-----------+ * * +-----------+ * +-----------+ | Files | * * * * * * * * * | Comments | +-----------+ * +-----------+ * +-----------+ * | ... | * * * * * +-----------+
이런 상황에서는 다음과 같이 Comments
테이블에 연관되는 테이블 별로 칼럼을 생성하게 되거나, (A)
CREATE TABLE Comments ( -- 중략 document_id BIGINT UNSIGNED, file_id BIGINT UNSIGNED, -- 중략 FOREIGN KEY (document_id) REFERENCES Documents(id), FOREIGN KEY (file_id) REFERENCES Files(id), )
혹은 다음과 같이 연관 대상을 지정하는 칼럼이 생길 수도 있습니다. (B)
CREATE TABLE Comments ( -- 중략 ref_type VARCHAR(20) NOT NULL, ref_id BIGINT UNSIGNED, -- 후략 CHECK (ref_type IN ('Documents', 'Files')) )
(A)와 (B) 모두 그다지 깔끔한 방법이 아닙니다. 양쪽 모두 연관이 추가될 때마다 스키마를 수정해야 됩니다. (A)에는 불필요한 칼럼이 많아서 무수한 NULL
들이 값으로 삽입되고 저장 공간도 낭비됩니다. (B)에서는 참조정합성을 강제할 수 없습니다.
그러므로 다음과 같이 수퍼테이블을 생성하고 연관은 서브테이블이 아닌 수퍼테이블과 생성하는 것이 좋습니다.
+-----------+ +-----------+ | Articles | * * * * * * * * * | Comments | +-----------+ +-----------+ * * +-----------+ * * * * * | Files | * +-----------+ * * +-----------+ * * * * * | Documents | * +-----------+ * * +-----------+ * * * * * | ... | +-----------+
이러한 방식을 사용하면 다음과 같은 장점들이 있습니다.