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

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

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

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

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

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

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

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

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



답변

@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 에서 왔습니다 .


답변

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

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


답변

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

정책

  • POLICY_ID (기본 키)

부채

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

속성

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

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

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

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

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


답변

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

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

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


답변

다른 방법은 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);

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


답변

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

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

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


답변

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