데이터베이스에서 상속을 어떻게 나타낼 수 있습니까?


236

SQL Server 데이터베이스에서 복잡한 구조를 나타내는 방법에 대해 생각하고 있습니다.

일부 속성은 공유하지만 다른 속성은 많지 않은 객체 제품군의 세부 정보를 저장해야하는 응용 프로그램을 고려하십시오. 예를 들어, 상업용 보험 패키지에는 동일한 정책 기록 내에 책임, 자동차, 재산 및 손해 배상 커버가 포함될 수 있습니다.

섹션 모음을 사용하여 정책을 만들 수 있으므로 C # 등에서이를 구현하는 것은 쉽지 않습니다. 여기서 섹션은 다양한 유형의 표지에 필요한대로 상속됩니다. 그러나 관계형 데이터베이스는이를 쉽게 허용하지 않는 것 같습니다.

두 가지 주요 선택이 있음을 알 수 있습니다.

  1. 가능한 모든 변형에 대해 필요한 모든 필드와 함께 Policy 테이블을 생성 한 다음 Sections 테이블을 생성하십시오. 대부분의 경우 null입니다.

  2. 각 커버 종류마다 하나씩 정책 테이블과 수많은 섹션 테이블을 만듭니다.

이러한 모든 대안은 만족스럽지 못합니다. 특히 모든 조인에 걸쳐 쿼리를 작성해야하는데, 여기에는 수많은 조인 또는 수많은 null 검사가 포함됩니다.

이 시나리오에 대한 모범 사례는 무엇입니까?


답변:


430

@Bill Karwin 은 SQL 엔터티-속성-값 안티 패턴 에 대한 솔루션을 제안 할 때 그의 SQL 안티 패턴 책 에서 세 가지 상속 모델을 설명 합니다. 이것은 간단한 개요입니다.

단일 테이블 상속 (일명 계층 당 테이블 상속) :

첫 번째 옵션 에서처럼 단일 테이블을 사용하는 것이 가장 간단한 디자인 일 것입니다. 언급했듯이 하위 유형별 속성은 해당 NULL속성이 적용되지 않는 행에 값을 지정해야합니다. 이 모델을 사용하면 다음과 같은 정책 테이블이 하나 생깁니다.

+------+---------------------+----------+----------------+------------------+
| id   | date_issued         | type     | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
|    1 | 2010-08-20 12:00:00 | MOTOR    | 01-A-04004     | NULL             |
|    2 | 2010-08-20 13:00:00 | MOTOR    | 02-B-01010     | NULL             |
|    3 | 2010-08-20 14:00:00 | PROPERTY | NULL           | Oxford Street    |
|    4 | 2010-08-20 15:00:00 | MOTOR    | 03-C-02020     | NULL             |
+------+---------------------+----------+----------------+------------------+

\------ COMMON FIELDS -------/          \----- SUBTYPE SPECIFIC FIELDS -----/

디자인을 단순하게 유지하는 것이 장점이지만이 방법의 주요 문제점은 다음과 같습니다.

  • 새로운 하위 유형을 추가 할 때는 이러한 새 객체를 설명하는 속성을 수용하도록 테이블을 변경해야합니다. 하위 유형이 많거나 정기적으로 하위 유형을 추가하려는 경우 문제가 발생할 수 있습니다.

  • 어떤 속성이 어떤 하위 유형에 속하는지 정의 할 메타 데이터가 없기 때문에 데이터베이스는 어떤 속성이 적용되고 어떤 속성을 적용하지 않을 수 없습니다.

  • 또한 NOT NULL필수이어야하는 하위 유형의 속성을 적용 할 수 없습니다 . 일반적으로 적합하지 않은 응용 프로그램에서이를 처리해야합니다.

콘크리트 테이블 상속 :

상속을 처리하는 또 다른 방법은 각 하위 유형에 대해 새 테이블을 작성하고 각 테이블의 모든 공통 속성을 반복하는 것입니다. 예를 들면 다음과 같습니다.

--// Table: policies_motor
+------+---------------------+----------------+
| id   | date_issued         | vehicle_reg_no |
+------+---------------------+----------------+
|    1 | 2010-08-20 12:00:00 | 01-A-04004     |
|    2 | 2010-08-20 13:00:00 | 02-B-01010     |
|    3 | 2010-08-20 15:00:00 | 03-C-02020     |
+------+---------------------+----------------+
                          
--// Table: policies_property    
+------+---------------------+------------------+
| id   | date_issued         | property_address |
+------+---------------------+------------------+
|    1 | 2010-08-20 14:00:00 | Oxford Street    |   
+------+---------------------+------------------+

이 설계는 기본적으로 단일 테이블 방법에 대해 식별 된 문제점을 해결합니다.

  • 에 필수 속성을 적용 할 수 있습니다 NOT NULL.

  • 새 하위 유형을 추가하려면 기존 유형에 열을 추가하는 대신 새 테이블을 추가해야합니다.

  • vehicle_reg_no속성 정책 의 필드 와 같은 특정 하위 유형에 대해 부적절한 속성이 설정 될 위험도 없습니다 .

  • type단일 테이블 메소드에서와 같이 속성이 필요하지 않습니다 . 유형은 이제 메타 데이터 (테이블 이름)로 정의됩니다.

그러나이 모델에는 몇 가지 단점이 있습니다.

  • 공통 속성은 하위 유형별 속성과 혼합되며이를 식별하는 쉬운 방법은 없습니다. 데이터베이스도 알 수 없습니다.

  • 테이블을 정의 할 때 각 하위 유형 테이블에 대한 공통 속성을 반복해야합니다. 그것은 확실히 건조 하지 않습니다 .

  • 하위 유형에 관계없이 모든 정책을 검색하기가 어려워지고 많은 정책이 필요합니다 UNION.

다음은 유형에 관계없이 모든 정책을 쿼리하는 방법입니다.

SELECT     date_issued, other_common_fields, 'MOTOR' AS type
FROM       policies_motor
UNION ALL
SELECT     date_issued, other_common_fields, 'PROPERTY' AS type
FROM       policies_property;

새 하위 유형을 추가하려면 UNION ALL각 하위 유형마다 추가 로 위의 쿼리를 수정해야하는 방법에 유의하십시오 . 이 작업을 잊어 버린 경우 응용 프로그램에 쉽게 버그가 발생할 수 있습니다.

클래스 테이블 상속 (일명 유형 당 테이블 상속) :

이것은 @David가 다른 답변에서 언급 한 솔루션입니다 . 모든 공통 속성을 포함하는 기본 클래스에 대한 단일 테이블을 작성합니다. 그런 다음 각 하위 유형에 대해 특정 테이블을 작성하고 기본 키는 기본 테이블 의 외래 키 역할도합니다 . 예:

CREATE TABLE policies (
   policy_id          int,
   date_issued        datetime,

   -- // other common attributes ...
);

CREATE TABLE policy_motor (
    policy_id         int,
    vehicle_reg_no    varchar(20),

   -- // other attributes specific to motor insurance ...

   FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);

CREATE TABLE policy_property (
    policy_id         int,
    property_address  varchar(20),

   -- // other attributes specific to property insurance ...

   FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);

이 솔루션은 다른 두 가지 디자인에서 식별 된 문제를 해결합니다.

  • 필수 속성은으로 시행 할 수 있습니다 NOT NULL.

  • 새 하위 유형을 추가하려면 기존 유형에 열을 추가하는 대신 새 테이블을 추가해야합니다.

  • 특정 하위 유형에 부적절한 속성이 설정 될 위험이 없습니다.

  • type속성이 필요하지 않습니다 .

  • 이제 공통 속성은 더 이상 하위 유형별 속성과 혼합되지 않습니다.

  • 마지막으로 DRY를 유지할 수 있습니다. 테이블을 작성할 때 각 하위 유형 테이블에 대해 공통 속성을 반복 할 필요가 없습니다.

  • id정책에 대한 자동 증분 관리는 각 하위 유형 테이블을 독립적으로 생성하는 대신 기본 테이블에서 처리 할 수 ​​있기 때문에 쉬워집니다.

  • 에 관계없이 지금은 매우 간단하게 하위 유형의 모든 정책을 검색 : 없음 UNION의 필요 - 단지를 SELECT * FROM policies.

클래스 테이블 접근 방식이 대부분의 상황에서 가장 적합하다고 생각합니다.


이 세 가지 모델의 이름은 Martin Fowler의Patterns of Enterprise Application Architecture 에서 왔습니다 .


97
이 디자인도 사용하고 있지만 단점은 언급하지 않았습니다. 구체적으로 : 1) 타입이 필요 없다고 말합니다. true이지만 일치하는 항목을 찾기 위해 모든 부속 유형 테이블을 보지 않으면 실제 유형의 행을 식별 할 수 없습니다. 2) 마스터 테이블과 하위 유형 테이블을 동기화 상태로 유지하기가 어렵습니다 (예 : 마스터 테이블이 아닌 하위 유형 테이블의 행을 제거 할 수 있음). 3) 각 마스터 행에 대해 둘 이상의 부속 유형을 가질 수 있습니다. 1을 해결하기 위해 트리거를 사용하지만 2와 3은 매우 어려운 문제입니다. 컴포지션을 모델링하는 경우 실제로 3은 문제가되지 않지만 엄격한 상속을위한 것입니다.

19
@Tibo의 의견에 +1하면 심각한 문제입니다. 클래스 테이블 상속은 실제로 정규화되지 않은 스키마를 생성합니다. 콘크리트 테이블 상속이 아닌 곳에서 콘크리트 테이블 상속이 DRY를 방해한다는 주장에 동의하지 않습니다. 메타 프로그래밍 기능이 없기 때문에 SQL 은 DRY를 방해합니다. 해결책은 SQL을 직접 작성하는 대신 데이터베이스 툴킷을 사용하여 직접 작성하는 것입니다 (실제로는 DB 인터페이스 언어 임). 결국, 엔터프라이즈 응용 프로그램도 어셈블리로 작성하지 않습니다.
Jo So

18
@Tibo, 포인트 3에 대해서는 sqlteam.com/article/…에 설명 된 접근 방식을 사용할 수 있습니다 . 모델링 일대 제약 조건 섹션을 확인하십시오.
앤드류

4
@DanielVassallo 첫번째로 놀라운 답변에 감사드립니다. 사람이 policy_motor인지 policy_property인지 아는 방법을 알고 있습니까? 한 가지 방법은 모든 하위 테이블에서 policyId를 검색하는 것이지만 이것이 잘못된 방법이라고 생각합니다. 올바른 접근법은 무엇입니까?
ThomasBecker

11
나는 당신의 세 번째 옵션을 정말로 좋아합니다. 그러나 SELECT가 어떻게 작동하는지 혼란 스럽습니다. 정책 *를 선택하면 정책 ID가 반환되지만 정책이 속하는 하위 유형 테이블을 여전히 알 수 없습니다. 모든 정책 세부 정보를 얻기 위해 여전히 모든 하위 유형으로 JOIN을 수행하지 않아도됩니까?
Adam

14

세 번째 옵션은 "정책"테이블을 작성한 다음 섹션 유형에 공통 인 모든 필드를 저장하는 "SectionsMain"테이블을 작성하는 것입니다. 그런 다음 공통적이지 않은 필드 만 포함하는 각 섹션 유형에 대해 다른 테이블을 작성하십시오.

가장 적합한 항목을 결정하는 것은 주로 보유하고있는 필드 수와 SQL 작성 방법에 따라 다릅니다. 그들은 모두 작동합니다. 몇 개의 필드가 있다면 # 1로 갈 것입니다. 필드의 "많은"을 사용하면 # 2 또는 # 3으로 기대 게됩니다.


+1 : 3 번째 옵션은 상속 모델에 가장 가까운 옵션이며 가장 표준화 된 IMO
RedFilter

옵션 # 3은 실제로 옵션 # 2의 의미입니다. 많은 필드가 있으며 일부 섹션에는 하위 엔티티도 있습니다.
Steve Jones

9

제공된 정보를 사용하여 다음을 갖도록 데이터베이스를 모델링했습니다.

정책

  • POLICY_ID (기본 키)

부채

  • LIABILITY_ID (기본 키)
  • POLICY_ID (외부 키)

속성

  • PROPERTY_ID (기본 키)
  • POLICY_ID (외부 키)

... 정책의 각 섹션과 관련된 다른 속성이있을 것으로 기대하기 때문에 등. 그렇지 않으면 단일 SECTIONS테이블 이있을 수 있으며 에 추가 policy_id하여 section_type_code...

어느 쪽이든, 이것은 정책 당 선택적 섹션을 지원할 수 있습니다 ...

이 방법에 대해 불만족스러운 점을 이해하지 못합니다. 이는 데이터를 복제하지 않고 참조 무결성을 유지하면서 데이터를 저장하는 방법입니다. 이 용어는 "정규화 된"입니다 ...

SQL은 SET 기반이기 때문에 절차 / OO 프로그래밍 개념에 익숙하지 않으며 한 영역에서 다른 영역으로 전환하는 코드가 필요합니다. ORM은 종종 고려되지만 대량의 복잡한 시스템에서는 제대로 작동하지 않습니다.


예, 정규화 문제가 있습니다. ;-) 복잡한 구조의 경우 일부 섹션이 단순하고 일부는 자체 하위 구조를 가지고 있기 때문에 ORM이 작동하는 것처럼 보이지는 않습니다.
Steve Jones

6

또한 Daniel Vassallo 솔루션에서 SQL Server 2016+를 사용하는 경우 성능 저하없이 상당한 경우에 사용했던 또 다른 솔루션이 있습니다.

공통 필드 만있는 테이블 만 생성 하고 모든 하위 유형별 필드를 포함 하는 JSON 문자열이 있는 단일 열을 추가 할 수 있습니다.

상속 관리를 위해이 디자인을 테스트했으며 상대 응용 프로그램에서 사용할 수있는 유연성에 매우 만족합니다.


1
흥미로운 생각입니다. 아직 SQL Server에서 JSON을 사용하지 않았지만 다른 곳에서는 많이 사용합니다. 고마워요
Steve Jones

5

다른 방법은 INHERITS구성 요소를 사용하는 것 입니다. 예를 들면 다음과 같습니다.

CREATE TABLE person (
    id int ,
    name varchar(20),
    CONSTRAINT pessoa_pkey PRIMARY KEY (id)
);

CREATE TABLE natural_person (
    social_security_number varchar(11),
    CONSTRAINT pessoaf_pkey PRIMARY KEY (id)
) INHERITS (person);


CREATE TABLE juridical_person (
    tin_number varchar(14),
    CONSTRAINT pessoaj_pkey PRIMARY KEY (id)
) INHERITS (person);

따라서 테이블 간 상속을 정의 할 수 있습니다.


PostgreSQLINHERITS 외에 다른 DB도 지원합니까 ? 예를 들어 MySQL ?
giannis christofakis

1
@giannischristofakis : MySQL은 관계형 데이터베이스 일 뿐이며 Postgres는 객체 관계형 데이터베이스입니다. 따라서 MySQL은 이것을 지원하지 않습니다. 실제로 Postgres는 이러한 유형의 상속을 지원하는 유일한 최신 DBMS라고 생각합니다.
a_horse_with_no_name

2
@ marco-paulo-ollivier, OP의 질문은 SQL Server에 관한 것이므로 Postgres에서만 작동하는 솔루션을 제공하는 이유를 이해할 수 없습니다. 분명히, 문제를 해결하지 않습니다.
mapto

@mapto이 질문은 "데이터베이스에서 OO 스타일 상속을하는 방법"이라는 목표가되었습니다. 그것은 SQL 서버가 지금 무관 가능성에 대해 처음이었다
카이 우스 JARD

0

모든 섹션이 포함 된 전체 정책을 효율적으로 검색하기 위해 방법 # 1 (통합 섹션 테이블)에 의존합니다 (시스템이 많이 수행한다고 가정).

또한 사용중인 SQL Server 버전을 모르지만 2008+ 스파 스 열열의 많은 값이 NULL 인 상황에서 성능을 최적화하는 데 도움이됩니다.

궁극적으로 정책 섹션이 "유사한"정도를 결정해야합니다. 그것들이 실질적으로 다르지 않으면,보다 표준화 된 솔루션은 가치가있는 것보다 더 어려울 수 있다고 생각합니다 ...하지만 당신 만이 그 전화를 걸 수 있습니다. :)


전체 정책을 한 번에 제시하기에는 너무 많은 정보가 있으므로 전체 레코드를 검색 할 필요가 없습니다. 다른 프로젝트에서 2008의 스파 스를 사용했지만 2005 년이라고 생각합니다.
Steve Jones

"통합 섹션 테이블"이라는 용어는 어디에서 유래합니까? 구글은 거의 결과를 보여주지 않으며 이미 혼란스러운 용어가 있습니다.
Stephan-v

-1

또는 풍부한 데이터 구조 및 중첩을 기본적으로 지원하는 문서 데이터베이스 (예 : MongoDB)를 사용해보십시오.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.