Jak skutecznie modelować dziedziczenie w bazie danych?

Jakie są najlepsze praktyki modelowania dziedziczenia w bazach danych?

Jakie są kompromisy (np. queriability)?

(najbardziej interesuje mnie SQL Server i. NET, ale chcę również zrozumieć, jak inne platformy rozwiązują ten problem.)

Author: Even Mien, 2008-10-10

9 answers

Istnieje kilka sposobów modelowania dziedziczenia w bazie danych. To, co wybierzesz, zależy od twoich potrzeb. Oto kilka opcji:

Table-Per-Type (TPT)

Każda klasa ma własną tabelę. Klasa bazowa ma w sobie wszystkie elementy klasy bazowej, a każda klasa, która z niej pochodzi, ma swoją własną tabelę, z kluczem podstawowym, który jest również kluczem obcym do tabeli klasy bazowej; Klasa tabeli pochodnej zawiera tylko różne elementy.

Więc dla przykład:

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

class Employee : Person {
    public DateTime StartDate;
}

Wynikałoby z tabel takich jak:

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

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

Table-Per-Hierarchy (TPH)

Istnieje jedna tabela, która reprezentuje całą hierarchię dziedziczenia, co oznacza, że kilka kolumn będzie prawdopodobnie rzadkich. Dodawana jest kolumna dyskryminująca, która informuje system, jakiego typu jest to wiersz.

Biorąc pod uwagę powyższe klasy, kończysz z tą tabelą:

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

Dla wszystkich wierszy, które są rowtype 0 (Person), Data startowa będzie zawsze null.

Table-Per-Concrete (TPC)

Każda klasa ma własną, w pełni uformowaną tabelę bez odniesień do innych tabel.

Biorąc pod uwagę powyższe klasy, kończysz z tymi tabelami:

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

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate
 125
Author: Brad Wilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 06:11:28

Poprawne projektowanie baz danych nie jest niczym innym jak poprawne Projektowanie obiektów.

Jeśli planujesz użyć bazy danych do czegoś innego niż tylko serializacja obiektów (takich jak raporty, zapytania, Wykorzystanie wielu aplikacji, Business intelligence itp.) w takim razie nie polecam żadnego prostego mapowania z obiektów do tabel.

Wiele osób myśli o wierszu w tabeli bazy danych jako encji( spędziłem wiele lat myśląc w tych kategoriach), ale wiersz nie jest encją. To propozycja. Relacja bazy danych (tj. tabela) reprezentuje pewne stwierdzenie faktu o świecie. Obecność wiersza wskazuje, że fakt jest prawdziwy (i odwrotnie, jego brak wskazuje, że fakt jest fałszywy).

Dzięki temu zrozumieniu można zauważyć, że pojedynczy typ w programie obiektowym może być przechowywany w kilkunastu różnych relacjach. I różne typy (połączone przez dziedziczenie, skojarzenie, agregację lub całkowicie niezrzeszone) mogą być częściowo przechowywane w jednym pokrewieństwo.

Najlepiej zadać sobie pytanie, jakie fakty chcesz przechowywać, na jakie pytania chcesz uzyskać odpowiedzi, jakie raporty chcesz generować.

Po utworzeniu odpowiedniego projektu DB, proste jest tworzenie zapytań/widoków, które pozwalają serializować obiekty do tych relacji.

Przykład:

W systemie rezerwacji hotelowej może być konieczne zapisanie faktu, że Jane Doe ma rezerwację pokoju w Seaview Inn na 10-12 kwietnia. Czy to atrybut jednostki klienta? Czy jest to atrybut bytu hotelowego? Czy jest to podmiot rezerwacyjny z obiektami, które obejmują klienta i hotel? Może to być dowolna lub wszystkie te rzeczy w systemie zorientowanym obiektowo. W bazie danych nie jest to żadna z tych rzeczy. To po prostu nagi fakt.

Aby zobaczyć różnicę, rozważ następujące dwa zapytania. (1) Ile rezerwacji hotelowych ma Jane Doe na przyszły rok? (2) Ile pokoi jest zarezerwowanych na 10 kwietnia w Seaview Inn?

W systemie obiektowym query (1) jest atrybutem jednostki klienta, a query (2) jest atrybutem jednostki hotelowej. Są to obiekty, które ujawniłyby te właściwości w ich interfejsach API. (Choć oczywiście wewnętrzne mechanizmy, za pomocą których te wartości są uzyskiwane, mogą obejmować odniesienia do innych obiektów.)

W relacyjnym systemie bazodanowym, oba zapytania sprawdzają relację rezerwacji, aby uzyskać ich numery i koncepcyjnie nie ma potrzeby kłopotać się z każdym innym "podmiotem".

Tak więc, poprzez próbę przechowywania faktów o świecie-zamiast próbować przechowywać byty z atrybutami-powstaje właściwa relacyjna baza danych. A gdy jest prawidłowo zaprojektowany, to przydatne zapytania, które zostały niewyobrażalne w fazie projektowania mogą być łatwo skonstruowane, ponieważ wszystkie fakty potrzebne do spełnienia tych zapytań są we właściwych miejscach.

 99
Author: Jeffrey L Whitledge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 21:11:08

Krótka odpowiedź: nie.

Jeśli chcesz serializować swoje obiekty, Użyj ORM, a nawet lepszego czegoś takiego jak activerecord lub prevaylence.

Jeśli chcesz przechowywać dane, przechowuj je w sposób relacyjny (uważając na to, co przechowujesz i zwracając uwagę na to, co Jeffrey L Whitledge właśnie powiedział), a nie taki, na który ma wpływ twój projekt obiektu.

 8
Author: Marcin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 22:26:10

Wzory TPT, TPH i TPC są drogami, o których wspomniał Brad Wilson. Ale kilka uwag:

  • Klasy potomne dziedziczące z klasy bazowej mogą być postrzegane jako słabe encje do definicji klasy bazowej w bazie danych, co oznacza, że są zależne od swojej klasy bazowej i nie mogą bez niej istnieć. Widziałem wiele razy, że unikalne identyfikatory są przechowywane dla każdej tabeli potomnej, a także trzymając FK do tabeli rodzica. Jeden FK wystarczy i jeszcze lepiej niech on-delete Cascade włącza dla FK-relacji między tabelami potomnymi i bazowymi.

  • W TPT, widząc tylko rekordy tabeli bazowej, nie można znaleźć klasy potomnej, którą reprezentuje rekord. Jest to czasami potrzebne, gdy chcesz załadować listę wszystkich rekordów (bez robienia select na każdej tabeli potomnej). Jednym ze sposobów radzenia sobie z tym, jest posiadanie jednej kolumny reprezentującej typ klasy potomnej (podobnie jak Pole rowType w TPH), więc mieszanie TPT i TPH jakoś.

Powiedzmy, że chcemy zaprojektować bazę danych zawierającą następujący diagram klasy kształtów:

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;
}

KONSTRUKCJA bazy danych dla powyższych klas może wyglądać następująco:

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;
 5
Author: imang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-22 17:37:17

Istnieją dwa główne typy dziedziczenia, które można skonfigurować w DB, tabeli dla encji i tabeli dla hierarchii.

Tabela dla encji to miejsce, w którym masz podstawową tabelę encji, która ma wspólne właściwości wszystkich klas podrzędnych. Każda klasa potomna ma inną tabelę, z której każda ma tylko właściwości mające zastosowanie do tej klasy. Są one połączone 1: 1 przez ich PK

Alt text http://mattlant.com/ent.png

Tabela na hierarchię to miejsce, w którym wszystkie klasy współdzieliły tabelę, a opcjonalne właściwości są nullable. Ich jest również polem dyskryminującym, które jest liczbą oznaczającą typ, który obecnie posiada rekord

Alt text http://mattlant.com/hier.png SessionTypeID is discriminator

Target na hierarchię jest szybszy do wyszukiwania, ponieważ nie potrzebujesz joins(tylko wartość dyskryminatora), podczas gdy target na encję musisz wykonać złożone joins, aby wykryć, jaki jest typ czegoś, a także odzyskać wszystkie jego dane..

Edit: obrazki I Pokaż tutaj są zrzuty ekranu projektu, nad którym pracuję. Obraz aktywów nie jest kompletny, stąd pustka to, ale to było głównie po to, aby pokazać, jak jego konfiguracji, a nie co umieścić wewnątrz tabel. To zależy od Ciebie ;). Tabela sesji zawiera informacje o wirtualnej sesji współpracy i może składać się z kilku rodzajów sesji, w zależności od rodzaju współpracy.

 4
Author: mattlant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 07:21:00

Znormalizowałbyś swoją bazę danych, a to odzwierciedlałoby twoje dziedzictwo. Może mieć degradację wydajności, ale tak to jest z normalizacją. Prawdopodobnie będziesz musiał użyć zdrowego rozsądku, aby znaleźć równowagę.

 1
Author: Per Hornshøj-Schierbeck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 06:06:01

Powtórz podobną odpowiedź wątku

W mapowaniu O-R, dziedziczenie mapuje do tabeli nadrzędnej, gdzie tabele nadrzędne i podrzędne używają tego samego identyfikatora

Na przykład

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)
)

Obiekt podrzędny ma relację z kluczem obcym do obiektu. gdy tworzysz wiersz Podobiektu, musisz najpierw utworzyć wiersz obiektu i użyć Id w obu wierszach

EDIT: jeśli chcesz również modelować zachowanie, potrzebujesz tabeli typu, która zawiera relacje dziedziczenia między tabel, a także określiła asemblację i nazwę klasy, która zaimplementowała zachowanie każdej tabeli

Wydaje się przesadą, ale wszystko zależy od tego, do czego chcesz go użyć!

 1
Author: Steven A. Lowe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:34

Używając SQL ALchemy (Python ORM), możesz wykonać dwa rodzaje dziedziczenia.

To, co miałem doświadczenie, to używanie tabeli singe i posiadanie dyskryminującej kolumny. Na przykład baza danych owiec (nie żart!) przechowywano wszystkie owce w jednej tabeli, a barany i owce były obsługiwane za pomocą kolumny płci w tej tabeli.

W ten sposób możesz zapytać o wszystkie owce i uzyskać wszystkie owce. Lub możesz odpytywać tylko za pomocą pamięci Ram, a otrzyma tylko pamięci RAM. Można również robić rzeczy takie jak mieć związek, który może tylko Baran (tj. ojciec owiec), i tak dalej.

 1
Author: Matthew Schinckel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 08:28:53

Zauważ, że niektóre silniki bazodanowe już natywnie dostarczają mechanizmy dziedziczenia, takie jak Postgres . Spójrz na dokumentację .

Dla przykładu, zapytałbyś o system osoby / pracownika opisany w odpowiedzi powyżej w następujący sposób:

  /* This shows the first name of all persons or employees */
  SELECT firstname FROM Person ; 

  /* This shows the start date of all employees only */
  SELECT startdate FROM Employee ;

W tym jest wybór bazy danych, nie musisz być szczególnie mądry !

 1
Author: Pierre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 09:10:54