데이터베이스에서 상속을 어떻게 효과적으로 모델링합니까?


131

데이터베이스에서 상속을 모델링하는 모범 사례는 무엇입니까?

트레이드 오프 (예 : 쿼리 가능성) 란 무엇입니까?

(SQL Server 및 .NET에 가장 관심이 있지만 다른 플랫폼에서이 문제를 해결하는 방법도 알고 싶습니다.)


14
"모범 사례"에 관심이있는 경우 대부분의 답변이 잘못되었습니다. 모범 사례는 RDb와 앱이 독립적이라는 것을 나타냅니다. 그들은 완전히 다른 디자인 기준을 가지고 있습니다. 따라서 데이터베이스의 "상속 모델링"(또는 단일 앱 또는 앱 언어에 맞게 RDb 모델링)은 매우 나쁜 습관이며, 정보가 없으며, 기본 RDb 디자인 규칙을 위반하고이를 무너 뜨립니다.
PerformanceDBA


6
@PerformanceDBA 그렇다면 DB 모델에서 상속을 피하기 위해 당신의 제안은 무엇입니까? 50 가지 유형의 교사가 있으며 특정 교사를 수업에 연결하려고한다고 가정 해 보겠습니다. 상속받지 않고 어떻게 그것을 달성하겠습니까?
svlada

1
@svlada. RDb에서 구현하는 것은 간단하므로 "상속"이 필요합니다. 질문을하고, 테이블 정의와 예제를 포함 시키십시오. 그러면 자세히 대답 할 것입니다. 당신이 OO 용어로 그것을한다면, 그것은 엉망이 될 것입니다.
PerformanceDBA

답변:


162

데이터베이스에서 상속을 모델링하는 방법에는 여러 가지가 있습니다. 당신이 선택하는 것은 당신의 필요에 달려 있습니다. 몇 가지 옵션이 있습니다.

유형별 테이블 (TPT)

각 클래스에는 자체 테이블이 있습니다. 기본 클래스에는 모든 기본 클래스 요소가 있으며 기본 클래스 요소에서 파생되는 각 클래스에는 기본 테이블이 있으며 기본 클래스 테이블의 외래 키이기도 한 기본 키가 있습니다. 파생 테이블의 클래스는 다른 요소 만 포함합니다.

예를 들어 :

class Person {
    public int ID;
    public string FirstName;
    public string LastName;
}

class Employee : Person {
    public DateTime StartDate;
}

다음과 같은 테이블이 생성됩니다.

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK, FK)
datetime startdate

계층 당 테이블 (TPH)

모든 상속 계층 구조를 나타내는 단일 테이블이 있습니다. 이는 여러 열이 희소 함을 의미합니다. 시스템에 이것이 어떤 행 유형인지 알려주는 판별 자 열이 추가됩니다.

위의 클래스가 주어지면이 테이블로 끝납니다.

table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate

rowtype 0 (Person) 인 행의 경우 startdate는 항상 null입니다.

콘크리트 당 테이블 (TPC)

각 클래스에는 다른 테이블에 대한 참조가없는 자체 구성 테이블이 있습니다.

위의 클래스가 주어지면 다음 표로 끝납니다.

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate

23
'선택하는 것이 귀하의 요구에 달려 있습니다.'-선택의 이유가 질문의 핵심을 형성한다고 생각하므로 정교하게 작성하십시오.
Alex

12
질문에 대한 내 의견을 참조하십시오. 존재하는 Rdb 기술 용어에 대해 재미있는 새로운 이름을 사용하면 혼란이 생깁니다. "TPT"는 수퍼 타입-서브 타입이다. "TPH"는 표준화되지 않은 오류입니다. "TPH"는 정규화가 덜되고 또 다른 심각한 오류가 발생합니다.
PerformanceDBA

45
DBA만이 비정규 화가 항상 오류라고 가정합니다. :)
Brad Wilson

7
비정규 화로 인해 일부 경우 성능이 향상되는 것은 사실이지만 DBMS에서 데이터의 논리적 구조와 물리적 구조가 불완전하게 (또는 존재하지 않는) 분리되어 있기 때문입니다. 불행히도 대부분의 상용 DBMS는이 문제로 어려움을 겪고 있습니다. @PerformanceDBA가 정확합니다. 비정규 화는 속도에 대한 데이터 일관성을 희생하는 판단 오류입니다. 안타깝게도 DBMS가 제대로 설계 되었다면 DBA 나 개발자가 절대로 선택하지 않아도됩니다. 기록을 위해 나는 DBA가 아니다.
케네스 코크란

6
@ 브래드 윌슨. 개발자 만이 "성능을 위해"비정규화할 수 있습니다. 종종, 그것은 비정규 화가 아니며, 진실은 그것이 비정규 화라는 것입니다. 비정규 화 또는 비정규 화는 오류이며, 이론에 의해 뒷받침되고 수백만에 의해 경험되는 사실은 "추정"이 아닙니다.
PerformanceDBA

133

적절한 데이터베이스 디자인은 적절한 개체 디자인과 다릅니다.

보고서, 쿼리, 다중 응용 프로그램 사용, 비즈니스 인텔리전스 등과 같은 개체를 단순히 직렬화하는 것 이외의 용도로 데이터베이스를 사용하려는 경우 개체에서 테이블로의 간단한 매핑은 권장하지 않습니다.

많은 사람들이 데이터베이스 테이블의 행을 엔터티로 생각하지만 (나는 몇 년 동안 그런 용어로 생각했지만) 행은 엔터티가 아닙니다. 제안입니다. 데이터베이스 관계 (예 : 테이블)는 세계에 대한 사실에 대한 진술을 나타냅니다. 행이 존재한다는 것은 사실이 사실임을 나타내며 반대로 존재하지 않으면 사실이 거짓임을 나타냅니다.

이러한 이해를 통해 객체 지향 프로그램의 단일 유형이 12 가지 다른 관계에 걸쳐 저장 될 수 있음을 알 수 있습니다. 그리고 다양한 유형 (상속, 연관, 집계 또는 완전히 연관되지 않은)이 단일 관계로 부분적으로 저장 될 수 있습니다.

어떤 사실을 저장하고 싶은지, 어떤 질문에 대답하고 싶은지, 어떤 보고서를 생성 할 것인지 스스로에게 물어 보는 것이 가장 좋습니다.

적절한 DB 디자인이 만들어지면 객체를 해당 관계로 직렬화 할 수있는 쿼리 / 뷰를 만드는 것이 간단합니다.

예:

호텔 예약 시스템에서 Jane Doe가 4 월 10-12 일 Seaview Inn의 객실을 예약한다는 사실을 저장해야 할 수도 있습니다. 이것이 고객 엔티티의 속성입니까? 호텔 엔티티의 속성입니까? 고객과 호텔이 포함 된 숙박 시설을 보유한 예약 법인입니까? 객체 지향 시스템에서 그러한 것들 중 일부 또는 전부가 될 수 있습니다. 데이터베이스에서는 그러한 것이 아닙니다. 그것은 단순한 사실입니다.

차이점을 확인하려면 다음 두 쿼리를 고려하십시오. (1) Jane Doe는 내년에 얼마나 많은 호텔 예약을합니까? (2) 4 월 10 일에 Seaview Inn에서 몇 개의 객실을 예약합니까?

객체 지향 시스템에서 쿼리 (1)은 고객 엔터티의 특성이고 쿼리 (2)는 호텔 엔터티의 특성입니다. 그것들은 API에서 해당 속성을 노출하는 객체입니다. (물론 이러한 값을 얻는 내부 메커니즘에는 다른 객체에 대한 참조가 포함될 수 있습니다.)

관계형 데이터베이스 시스템에서 두 쿼리는 예약 관계를 조사하여 숫자를 가져오고 개념적으로 다른 "엔티티"를 신경 쓸 필요가 없습니다.

따라서 적절한 관계형 데이터베이스가 구성되는 것은 속성이있는 엔티티를 저장하지 않고 세계에 대한 사실을 저장하려고 시도하는 것입니다. 그리고 일단 제대로 설계되면, 설계 단계에서 꿈꾸지 않은 유용한 질의를 쉽게 구성 할 수 있습니다. 이러한 질의를 수행하는 데 필요한 모든 사실이 적절한 위치에 있기 때문입니다.


12
+1 마지막으로, 무지의 바다에서 진정한 지식의 섬 (그리고 그들의 야망 밖에서 배우기를 거부 함). 합의는 마술이 아닙니다. RDb가 RDb 원칙을 사용하여 설계되면 "클래스"를 "매핑"하거나 "프로젝트"하기가 쉽지 않습니다. RDb를 클래스 기반 요구 사항으로 강요하는 것은 간단하지 않습니다.
PerformanceDBA

2
흥미로운 답변. 허용되는 답변에서 Person-Employee 예제를 모델링하는 방법을 제안 하시겠습니까?
sevenforce

2
@ sevenforce-DB 디자인은 실제로 시스템의 요구 사항에 달려 있으며, 제공되지 않습니다. 결정할 정보가 충분하지 않습니다. 많은 경우에 "유형별"디자인과 유사한 것이 노예로 따르지 않는 경우 적절할 수 있습니다. 예를 들어, start-date는 Employee 오브젝트에 대해 좋은 특성 일 수 있지만 데이터베이스에서 사람은 여러 시작 날짜로 여러 번 고용 될 수 있으므로 실제로 고용 테이블의 필드 여야합니다. 이것은 가장 최근에 사용되는 객체에는 중요하지 않지만 데이터베이스에서는 중요합니다.
Jeffrey L Whitledge

2
물론 제 질문은 상속을 모델링하는 방법에 관한 것이 었습니다. 충분히 명확하지 않아 죄송합니다. 감사. 언급했듯이, Employment모든 고용을 시작 날짜로 수집 하는 table 이 있어야합니다 . 따라서의 현재 고용 시작 날짜를 아는 Employer것이 중요 하다면 View, 이는 질의를 통해 해당 속성을 포함하는 의 적절한 사용 사례가 될 수 있습니까? (참고 : 내 닉 직후 '-'때문에 귀하의 의견에 대한 알림을받지 못했습니다)
sevenforce

5
이것은 답변의 진정한 보석입니다. 실제로 빠져들려면 시간이 필요하고 제대로 작동하려면 약간의 운동이 필요하지만 관계형 데이터베이스 디자인에 대한 나의 사고 과정에 이미 영향을 미쳤습니다.
MarioDS

9

짧은 대답 : 당신은하지 않습니다.

객체를 직렬화해야하는 경우 ORM을 사용하거나 액티브 레코드 또는 유행과 같은 것을 사용하십시오.

데이터를 저장해야하는 경우 객체 디자인의 영향을받지 않고 관계형 방식으로 저장합니다 (저장 대상에주의를 기울이고 Jeffrey L Whitledge가 말한 내용에주의를 기울임).


3
+1 데이터베이스에서 상속을 모델링하는 것은 좋은 관계형 자원을 낭비하는 것입니다.
Daniel Spiewak

7

Brad Wilson이 언급했듯이 TPT, TPH 및 TPC 패턴은 여러분이가는 길입니다. 그러나 몇 가지 메모 :

  • 기본 클래스에서 상속되는 자식 클래스는 데이터베이스의 기본 클래스 정의에 대한 취약한 항목으로 간주 될 수 있습니다. 즉, 기본 클래스에 종속되어 있으며 클래스가 없으면 존재할 수 없습니다. FK를 부모 테이블에 유지하면서 각 자식 테이블마다 고유 ID가 저장되는 횟수를 보았습니다. 하나의 FK로 충분하고 하위 테이블과 기본 테이블 간의 FK 관계에 대해 삭제시 계단식 사용을 가능하게하는 것이 훨씬 좋습니다.

  • TPT에서는 기본 테이블 레코드 만보고 레코드가 나타내는 하위 클래스를 찾을 수 없습니다. select 모든 하위 테이블에 대해 수행하지 않고 모든 레코드 목록을로드하려는 경우에 때때로 필요 합니다. 이것을 처리하는 한 가지 방법은 자식 클래스의 유형을 나타내는 하나의 열 (TPH의 rowType 필드와 유사)을 가지므로 TPT와 TPH를 어떻게 든 혼합하는 것입니다.

다음과 같은 형태 클래스 다이어그램을 보유한 데이터베이스를 설계하려고합니다.

public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}

public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}

public class Circle : Shape {
Point center;
int radius;
}

위 클래스의 데이터베이스 디자인은 다음과 같습니다.

table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)

table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;

table Circle
----------
int ShapeID; (FK on delete cascade)  
int centerX;
int center;
int radius;

4

DB에서 설정할 수있는 두 가지 주요 상속 유형은 엔터티 당 테이블과 계층 당 테이블입니다.

엔터티 당 테이블에는 모든 자식 클래스의 공유 속성이있는 기본 엔터티 테이블이 있습니다. 그런 다음 하위 클래스마다 각각 해당 클래스에 적용 가능한 속성 만있는 다른 테이블이 있습니다. 그들은 그들의 PK에 의해 1 : 1로 연결되어 있습니다

대체 텍스트

계층 당 테이블은 모든 클래스가 테이블을 공유하는 곳이며 선택적 속성은 Null을 허용합니다. 또한 레코드가 현재 보유하고있는 유형을 나타내는 숫자 인 판별 기 필드이기도합니다.

대체 텍스트 SessionTypeID는 차별 자입니다

계층 당 대상은 조인이 필요하지 않기 때문에 쿼리하는 것이 더 빠르며 (식별자 값만 해당) 엔티티 별 대상은 어떤 유형의 데이터를 검색하고 모든 데이터를 검색하기 위해 복잡한 조인을 수행해야합니다.

편집 : 여기에 표시된 이미지는 내가 작업중 인 프로젝트의 스크린 샷입니다. 애셋 이미지는 완성되지 않았으므로 비어 있습니다. 그러나 주로 테이블에 넣을 내용이 아니라 설정 방법을 보여주는 것이 었습니다. 그건 너 한테 달렸지 ;). 세션 테이블은 가상 협업 세션 정보를 보유하며 어떤 협업 유형에 따라 여러 유형의 세션이 될 수 있습니다.


또한 Target per Concrete 클래스를 고려하여 상속을 제대로 모델링하지 않았으므로 표시하지 않았습니다.
mattlant

일러스트레이션의 출처를 추가 할 수 있습니까?
chryss

답변이 끝날 때 이야기하는 이미지는 어디에 있습니까?
Musa Haidari

1

데이터베이스를 정규화하면 실제로 상속이 반영됩니다. 성능 저하가있을 수 있지만 정규화 방식입니다. 균형을 찾으려면 상식을 잘 사용해야 할 것입니다.


2
사람들은 왜 데이터베이스 정규화가 성능을 저하 시킨다고 생각합니까? 사람들은 또한 DRY 원칙이 코드 성능을 저하 시킨다고 생각합니까? 이 오해는 어디에서 오는가?
Steven A. Lowe

1
비정규 화는 성능을 향상시킬 수 있기 때문에 정규화하면 성능이 저하되고 상대적으로 저하됩니다. 내가 그것에 동의한다고 말할 수는 없지만 아마도 그 일이 생겼을 것입니다.
Matthew Scharley

2
처음에는 정규화가 성능에 약간의 영향을 줄 수 있지만 시간이 지남에 따라 행 수가 증가함에 따라 효율적인 JOIN이 더 큰 테이블보다 성능이 뛰어 나기 시작합니다. 물론, 정규화는 일관성과 중복성 부족 등 다른 큰 이점을 가지고 있습니다.
Rob

1

유사한 스레드 응답의 반복

OR 매핑에서 상속은 부모 테이블과 자식 테이블이 동일한 식별자를 사용하는 부모 테이블에 매핑됩니다.

예를 들어

create table Object (
    Id int NOT NULL --primary key, auto-increment
    Name varchar(32)
)
create table SubObject (
    Id int NOT NULL  --primary key and also foreign key to Object
    Description varchar(32)
)

SubObject는 Object와 외래 키 관계를 갖습니다. SubObject 행을 만들 때는 먼저 Object 행을 만들고 두 행 모두에서 ID를 사용해야합니다

편집 : 동작을 모델링하려면 테이블 간의 상속 관계를 나열하고 각 테이블의 동작을 구현하는 어셈블리 및 클래스 이름을 지정하는 Type 테이블이 필요합니다.

과잉으로 보이지만, 모두 사용하고자하는 것에 달려 있습니다!


그 논의는 모델링 상속이 아니라 모든 테이블에 몇 개의 열을 추가하는 것에 관한 것이 었습니다. 나는 그 토론의 제목이 질문과 토론의 본질을 더 잘 반영하도록 변경되어야한다고 생각합니다.
심지어 Mien

1

SQL ALchemy (Python ORM)를 사용하면 두 가지 유형의 상속을 수행 할 수 있습니다.

내가 경험 한 것은 단일 테이블을 사용하고 판별 열을 사용하는 것입니다. 예를 들어, 양 데이터베이스 (농담 없음)는 모든 양을 하나의 테이블에 저장했으며 Rams 및 Ewes는 해당 테이블의 성별 열을 사용하여 처리했습니다.

따라서 모든 양을 쿼리하고 모든 양을 얻을 수 있습니다. 또는 Ram으로 만 쿼리 할 수 ​​있으며 Ram 만 가져옵니다. 당신은 또한 단지 숫양 (즉, 양의 권자) 일 수있는 관계를 갖는 것과 같은 일을 할 수 있습니다.


1

일부 데이터베이스 엔진은 이미 Postgres 와 같은 상속 메커니즘을 제공합니다 . 상기 봐 문서 .

예를 들어, 위의 응답에서 설명한 Person / Employee 시스템을 다음과 같이 쿼리합니다.

  / * 모든 사람 또는 직원의 이름을 표시합니다 * /
  Person from FROM Person; 

  / * 모든 직원의 시작일 만 표시 * /
  직원에서 시작 날짜 선택;

그것이 당신의 데이터베이스 선택에서, 당신은 특별히 똑똑 할 필요는 없습니다!

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