데이터베이스에서 상속을 모델링하는 모범 사례는 무엇입니까?
트레이드 오프 (예 : 쿼리 가능성) 란 무엇입니까?
(SQL Server 및 .NET에 가장 관심이 있지만 다른 플랫폼에서이 문제를 해결하는 방법도 알고 싶습니다.)
데이터베이스에서 상속을 모델링하는 모범 사례는 무엇입니까?
트레이드 오프 (예 : 쿼리 가능성) 란 무엇입니까?
(SQL Server 및 .NET에 가장 관심이 있지만 다른 플랫폼에서이 문제를 해결하는 방법도 알고 싶습니다.)
답변:
데이터베이스에서 상속을 모델링하는 방법에는 여러 가지가 있습니다. 당신이 선택하는 것은 당신의 필요에 달려 있습니다. 몇 가지 옵션이 있습니다.
유형별 테이블 (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
적절한 데이터베이스 디자인은 적절한 개체 디자인과 다릅니다.
보고서, 쿼리, 다중 응용 프로그램 사용, 비즈니스 인텔리전스 등과 같은 개체를 단순히 직렬화하는 것 이외의 용도로 데이터베이스를 사용하려는 경우 개체에서 테이블로의 간단한 매핑은 권장하지 않습니다.
많은 사람들이 데이터베이스 테이블의 행을 엔터티로 생각하지만 (나는 몇 년 동안 그런 용어로 생각했지만) 행은 엔터티가 아닙니다. 제안입니다. 데이터베이스 관계 (예 : 테이블)는 세계에 대한 사실에 대한 진술을 나타냅니다. 행이 존재한다는 것은 사실이 사실임을 나타내며 반대로 존재하지 않으면 사실이 거짓임을 나타냅니다.
이러한 이해를 통해 객체 지향 프로그램의 단일 유형이 12 가지 다른 관계에 걸쳐 저장 될 수 있음을 알 수 있습니다. 그리고 다양한 유형 (상속, 연관, 집계 또는 완전히 연관되지 않은)이 단일 관계로 부분적으로 저장 될 수 있습니다.
어떤 사실을 저장하고 싶은지, 어떤 질문에 대답하고 싶은지, 어떤 보고서를 생성 할 것인지 스스로에게 물어 보는 것이 가장 좋습니다.
적절한 DB 디자인이 만들어지면 객체를 해당 관계로 직렬화 할 수있는 쿼리 / 뷰를 만드는 것이 간단합니다.
예:
호텔 예약 시스템에서 Jane Doe가 4 월 10-12 일 Seaview Inn의 객실을 예약한다는 사실을 저장해야 할 수도 있습니다. 이것이 고객 엔티티의 속성입니까? 호텔 엔티티의 속성입니까? 고객과 호텔이 포함 된 숙박 시설을 보유한 예약 법인입니까? 객체 지향 시스템에서 그러한 것들 중 일부 또는 전부가 될 수 있습니다. 데이터베이스에서는 그러한 것이 아닙니다. 그것은 단순한 사실입니다.
차이점을 확인하려면 다음 두 쿼리를 고려하십시오. (1) Jane Doe는 내년에 얼마나 많은 호텔 예약을합니까? (2) 4 월 10 일에 Seaview Inn에서 몇 개의 객실을 예약합니까?
객체 지향 시스템에서 쿼리 (1)은 고객 엔터티의 특성이고 쿼리 (2)는 호텔 엔터티의 특성입니다. 그것들은 API에서 해당 속성을 노출하는 객체입니다. (물론 이러한 값을 얻는 내부 메커니즘에는 다른 객체에 대한 참조가 포함될 수 있습니다.)
관계형 데이터베이스 시스템에서 두 쿼리는 예약 관계를 조사하여 숫자를 가져오고 개념적으로 다른 "엔티티"를 신경 쓸 필요가 없습니다.
따라서 적절한 관계형 데이터베이스가 구성되는 것은 속성이있는 엔티티를 저장하지 않고 세계에 대한 사실을 저장하려고 시도하는 것입니다. 그리고 일단 제대로 설계되면, 설계 단계에서 꿈꾸지 않은 유용한 질의를 쉽게 구성 할 수 있습니다. 이러한 질의를 수행하는 데 필요한 모든 사실이 적절한 위치에 있기 때문입니다.
Employment
모든 고용을 시작 날짜로 수집 하는 table 이 있어야합니다 . 따라서의 현재 고용 시작 날짜를 아는 Employer
것이 중요 하다면 View
, 이는 질의를 통해 해당 속성을 포함하는 의 적절한 사용 사례가 될 수 있습니까? (참고 : 내 닉 직후 '-'때문에 귀하의 의견에 대한 알림을받지 못했습니다)
짧은 대답 : 당신은하지 않습니다.
객체를 직렬화해야하는 경우 ORM을 사용하거나 액티브 레코드 또는 유행과 같은 것을 사용하십시오.
데이터를 저장해야하는 경우 객체 디자인의 영향을받지 않고 관계형 방식으로 저장합니다 (저장 대상에주의를 기울이고 Jeffrey L Whitledge가 말한 내용에주의를 기울임).
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;
DB에서 설정할 수있는 두 가지 주요 상속 유형은 엔터티 당 테이블과 계층 당 테이블입니다.
엔터티 당 테이블에는 모든 자식 클래스의 공유 속성이있는 기본 엔터티 테이블이 있습니다. 그런 다음 하위 클래스마다 각각 해당 클래스에 적용 가능한 속성 만있는 다른 테이블이 있습니다. 그들은 그들의 PK에 의해 1 : 1로 연결되어 있습니다
계층 당 테이블은 모든 클래스가 테이블을 공유하는 곳이며 선택적 속성은 Null을 허용합니다. 또한 레코드가 현재 보유하고있는 유형을 나타내는 숫자 인 판별 기 필드이기도합니다.
SessionTypeID는 차별 자입니다
계층 당 대상은 조인이 필요하지 않기 때문에 쿼리하는 것이 더 빠르며 (식별자 값만 해당) 엔티티 별 대상은 어떤 유형의 데이터를 검색하고 모든 데이터를 검색하기 위해 복잡한 조인을 수행해야합니다.
편집 : 여기에 표시된 이미지는 내가 작업중 인 프로젝트의 스크린 샷입니다. 애셋 이미지는 완성되지 않았으므로 비어 있습니다. 그러나 주로 테이블에 넣을 내용이 아니라 설정 방법을 보여주는 것이 었습니다. 그건 너 한테 달렸지 ;). 세션 테이블은 가상 협업 세션 정보를 보유하며 어떤 협업 유형에 따라 여러 유형의 세션이 될 수 있습니다.
데이터베이스를 정규화하면 실제로 상속이 반영됩니다. 성능 저하가있을 수 있지만 정규화 방식입니다. 균형을 찾으려면 상식을 잘 사용해야 할 것입니다.
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 테이블이 필요합니다.
과잉으로 보이지만, 모두 사용하고자하는 것에 달려 있습니다!
SQL ALchemy (Python ORM)를 사용하면 두 가지 유형의 상속을 수행 할 수 있습니다.
내가 경험 한 것은 단일 테이블을 사용하고 판별 열을 사용하는 것입니다. 예를 들어, 양 데이터베이스 (농담 없음)는 모든 양을 하나의 테이블에 저장했으며 Rams 및 Ewes는 해당 테이블의 성별 열을 사용하여 처리했습니다.
따라서 모든 양을 쿼리하고 모든 양을 얻을 수 있습니다. 또는 Ram으로 만 쿼리 할 수 있으며 Ram 만 가져옵니다. 당신은 또한 단지 숫양 (즉, 양의 권자) 일 수있는 관계를 갖는 것과 같은 일을 할 수 있습니다.