W zakresie, w jakim to pytanie jest kontynuacją Czy moja implementacja wzorca projektowego typu / podtypu (dla wzajemnie wykluczających się podklas) jest poprawna? , która sama w sobie jest kontynuacją Nie wiem, jak przekształcić zmienną jednostkę w relacyjną tabelę , zapytałbym: co dokładnie próbujesz zoptymalizować? Przechowywanie? Model obiektowy? Złożoność zapytania? Wydajność zapytania? Podczas optymalizacji jednego aspektu względem drugiego występują kompromisy, ponieważ nie można zoptymalizować wszystkich aspektów jednocześnie.
Całkowicie zgadzam się z punktami Remusa dotyczącymi:
- Każde podejście ma wady i zalety (tj. Zawsze obecny czynnik „zależy”), i
- Pierwszym priorytetem jest wydajność modelu danych (nieefektywnego modelu danych nie można poprawić za pomocą czystego i / lub wydajnego kodu aplikacji)
To powiedziawszy, wybór, przed którym stoisz, jest pomiędzy następującymi, ułożonymi w kolejności od najmniejszej normalizacji do większości normalizacji:
- promowanie właściwości
E
do tabeli typu podstawowego
- trzymanie go w wielu tabelach podtypów
- pełna normalizacja
E
do nowej, pośredniej tabeli podklas na tym samym poziomie co C
, A
i która B
będzie bezpośrednio podklasami (odpowiedź @ MDCCL )
Spójrzmy na każdą opcję:
Przenieś właściwość E
do tabeli typu podstawowego
PRO
- Zredukowana złożoność zapytań dla zapytań potrzeba
E
, ale nie X
, Y
albo Z
.
- Potencjalnie bardziej efektywne dla zapytań, które potrzebują
E
ale nie X
, Y
lub Z
(zwłaszcza kruszywa zapytań) ze względu na brak JOIN.
- Potencjał do utworzenia indeksu na
(D, E)
(a jeśli tak, potencjalnie indeksowany (D, E)
filtr, gdzie EntityType <> C
, jeśli taki warunek jest dozwolony)
Cons
- Nie można oznaczyć
E
jakoNOT NULL
- Potrzebujesz dodatkowej
CHECK CONSTRAINT
tabeli typu podstawowego, aby upewnić się, że E IS NULL
gdy EntityType = C
(choć nie jest to ogromny problem)
- Trzeba edukować użytkowników modelu danych, dlaczego
E
musi być NULL
, a nawet powinien być całkowicie ignorowany, gdy EntityType = C
.
- Nieco mniej wydajny, gdy
E
jest typu o stałej długości, a duża część wierszy dotyczy EntityType C
(tzn. Nie używa E
go zatem NULL
), i nie używa ani SPARSE
opcji w kolumnie, ani kompresji danych w indeksie klastrowym
- Potencjalnie mniej wydajny w przypadku zapytań, które nie są potrzebne,
E
ponieważ obecność E
w tabeli typu podstawowego zwiększy rozmiar każdego wiersza, co z kolei zmniejszy liczbę wierszy, które mogą zmieścić się na stronie danych. Jest to jednak wysoce zależne od dokładnego typu danych E
FILLFACTOR, liczby wierszy w tabeli typu bazowego itp.
Zachowaj właściwość E
w każdej tabeli podtypu
PRO
- Czystszy model danych (tj. Nie musisz się martwić o edukowanie innych, dlaczego kolumna
E
w tabeli typu podstawowego nie powinna być używana, ponieważ „tak naprawdę jej nie ma”)
- Prawdopodobnie bardziej przypomina model obiektowy
- Może oznaczyć kolumnę tak,
NOT NULL
jakby była to wymagana właściwość jednostki
- Nie ma potrzeby stosowania dodatkowych
CHECK CONSTRAINT
w tabeli typu podstawowego, aby upewnić się, że E IS NULL
gdy EntityType = C
(choć nie jest to ogromny zysk)
Cons
- Wymaga DOŁĄCZ do podtypu Tabele, aby uzyskać tę właściwość
- Potencjalnie nieco mniej wydajny, gdy jest potrzebny
E
, ze względu na JOIN, w zależności od liczby wierszy A
+ B
w przeciwieństwie do liczby wierszy C
.
- Nieco trudniejsze / bardziej skomplikowane dla operacji, które dotyczą wyłącznie podmiotów
A
i B
(a nie C
) jako tego samego „typu”. Oczywiście można to wyodrębnić za pomocą widoku, który wykonuje UNION ALL
między SELECT
tabelą JOINed dla A
innej SELECT
a tabelą JOINed dla B
. Że zmniejszy złożoność zapytań SELECT, ale nie tak pomocne INSERT
i UPDATE
zapytań.
- W zależności od konkretnych zapytań i częstotliwości ich wykonywania może to być potencjalna nieefektywność w przypadkach, w których posiadanie indeksu
(D, E)
naprawdę pomogłoby jednemu lub większej liczbie często używanych zapytań, ponieważ nie można ich indeksować razem.
Normalizuj E
do tabeli pośredniej między klasą bazową a A
&B
(Proszę zauważyć, że podoba mi się odpowiedź @ MDCCL jako realna alternatywa, w zależności od okoliczności. Poniższe informacje nie mają na celu surowej krytyki tego podejścia, ale jako sposób na dodanie perspektywy - oczywiście mojej - poprzez ocenę w tym samym kontekście, co dwie opcje, które już zaproponowałem. Ułatwi to wyjaśnienie, co postrzegam jako względną różnicę między pełną normalizacją a obecnym podejściem do częściowej normalizacji).
PRO
- model danych jest w pełni znormalizowany (nie może być w tym nic złego, biorąc pod uwagę, że właśnie do tego służą RDBMS)
- zmniejszona złożoność zapytań dla zapytań wymagających
A
i B
, ale nie C
(tj. nie ma potrzeby łączenia dwóch zapytań przez UNION ALL
)
Cons
- nieco więcej zajmowanego miejsca (
Bar
tabela duplikuje identyfikator i pojawia się nowa kolumna BarTypeCode
) [nieistotne, ale należy pamiętać]
- nieznaczny wzrost złożoności zapytań jako dodatkowy
JOIN
jest potrzebny, aby dostać się do jednego A
lub drugiegoB
- zwiększone pole powierzchni do blokowania, głównie włączone
INSERT
( DELETE
może być obsługiwane domyślnie poprzez oznaczenie kluczy obcych jako ON CASCADE DELETE
), ponieważ transakcja będzie utrzymywana otwarta dłużej na stole klasy podstawowej (tj. Foo
) [nieistotne, ale należy pamiętać o tym]
brak bezpośredniej wiedzy o rzeczywistym typie - A
lub B
- w obrębie tabeli klasy podstawowej Foo
; to wie tylko typu Br
, który może być A
albo B
:
Oznacza to, że jeśli musisz wykonać zapytania dotyczące ogólnych informacji podstawowych, ale musisz albo skategoryzować według typu jednostki, albo odfiltrować jeden lub więcej typów jednostek, wówczas tabela klasy podstawowej nie ma wystarczającej ilości informacji, w takim przypadku musisz stół. Zmniejszy to także skuteczność indeksowania kolumny.LEFT JOIN
Bar
FooTypeCode
brak spójnego podejścia do interakcji z A
& B
vs C
:
Oznacza to, że jeśli każdy byt odnosi się bezpośrednio do tabeli klasy podstawowej, tak że istnieje tylko jedno DOŁĄCZENIE, aby uzyskać pełny byt, wówczas każdy może szybciej i łatwiej zdobyć znajomość w zakresie pracy z modelem danych. Będzie powszechne podejście do zapytań / procedur przechowywanych, które przyspieszy ich rozwój i zmniejszy prawdopodobieństwo błędów. Spójne podejście ułatwia także dodawanie nowych podtypów w przyszłości.
potencjalnie mniej przystosowalny do reguł biznesowych, które zmieniają się z czasem:
Oznacza to, że rzeczy zawsze się zmieniają, a przejście E
do tabeli klasy podstawowej jest dość łatwe, jeśli stanie się wspólne dla wszystkich podtypów. Łatwo jest również przenieść wspólną właściwość do podtypów, jeśli zmiany w charakterze jednostek powodują, że warto to zmienić. Łatwo jest podzielić podtyp na dwa podtypy (po prostu utworzyć inną SubTypeID
wartość) lub połączyć dwa lub więcej podtypów w jeden. I odwrotnie, co jeśli E
później stałoby się wspólną własnością wszystkich podtypów? Wtedy warstwa pośrednia Bar
tabeli byłaby bez znaczenia, a dodatkowa złożoność nie byłaby tego warta. Oczywiście nie można wiedzieć, czy taka zmiana nastąpiłaby za 5 czy nawet 10 lat, więc Bar
tabela niekoniecznie, ani nawet prawdopodobnie nie będzie złym pomysłem (dlatego powiedziałem „ potencjalnie mniej adaptowalny”). To tylko punkty do rozważenia; jest to hazard w obu kierunkach.
potencjalnie nieodpowiednie grupowanie:
To znaczy, tylko dlatego, że E
właściwość jest współdzielona między typami jednostek A
i B
nie oznacza tego A
i B
powinna być zgrupowana razem. To, że rzeczy „wyglądają” tak samo (tj. Te same właściwości), nie oznacza, że są takie same.
streszczenie
Podobnie jak decyzja, czy / kiedy należy denormalizować, jak najlepiej podejść do tej konkretnej sytuacji, zależy od rozważenia następujących aspektów korzystania z modelu danych i upewnienia się, że korzyści przewyższają koszty:
- ile rzędów będziesz mieć dla każdego EntityType (spójrz co najmniej 5 lat w dół, zakładając ponadprzeciętny wzrost)
- ile GB będzie miała każda z tych tabel (typu podstawowego i podtypów) za 5 lat?
- jaki konkretny typ danych jest właściwością
E
- czy jest to tylko jedna właściwość, czy istnieje kilka, a nawet kilka właściwości
- jakie zapytania będą potrzebne
E
i jak często będą wykonywane
- jakich zapytań będziesz potrzebować, których nie potrzebujesz
E
i jak często będą wykonywane
Myślę, że raczej domyślnie trzymam się E
w osobnych tabelach podtypów, ponieważ jest to przynajmniej „czystsze”. Zastanowiłbym się nad przejściem E
do tabeli typu podstawowego IF: większość wierszy nie dotyczyła EntityType C
; a liczba rzędów była co najmniej w milionach; i częściej niż nie wykonywałem zapytań, które były potrzebne E
i / lub zapytań, które skorzystałyby z indeksu przy (D, E)
wykonywaniu bardzo często i / lub wymagały wystarczających zasobów systemowych, tak że posiadanie indeksu zmniejsza ogólne wykorzystanie zasobów lub przynajmniej zapobiega gwałtowne wzrosty zużycia zasobów przekraczające dopuszczalne poziomy lub trwające wystarczająco długo, aby spowodować nadmierne blokowanie i / lub wzrost impasu.
AKTUALIZACJA
OP skomentował tę odpowiedź, że:
Moi pracodawcy zmienili logikę biznesową, całkowicie usuwając E!
Zmiana ta jest szczególnie ważna, ponieważ jest to dokładnie to, co opiera się może zdarzyć w „minusy” podsekcji „normalizują E
się do tabeli pośredniczącej między bazowej klasy i A
& B
” Sekcja powyżej (podpunkt 6). Konkretnym problemem jest to, jak łatwo / trudno jest refaktoryzować model danych, gdy takie zmiany się zdarzają (i zawsze tak się dzieje). Niektórzy twierdzą, że każdy model danych może być refaktoryzowany / zmieniony, więc zacznij od ideału. Ale chociaż na poziomie technicznym prawdą jest, że wszystko można zrefaktoryzować, rzeczywistość sytuacji jest kwestią skali.
Zasoby nie są nieskończone, nie tylko procesor / dysk / pamięć RAM, ale także zasoby programistyczne: czas i pieniądze. Firmy stale ustalają priorytety dla projektów, ponieważ zasoby te są bardzo ograniczone. I dość często (przynajmniej z mojego doświadczenia) projekty mające na celu zwiększenie wydajności (nawet zarówno wydajność systemu, jak i szybszy rozwój / mniej błędów) są traktowane priorytetowo poniżej projektów, które zwiększają funkcjonalność. Choć jest to dla nas frustrujące dla ludzi technicznych, ponieważ rozumiemy, jakie są długoterminowe korzyści z projektów refaktoryzacyjnych, to po prostu charakter działalności sprawia, że mniej techniczni ludzie biznesu łatwiej dostrzegają bezpośredni związek między nową funkcjonalnością a nową dochód. Sprowadza się to do: „wrócimy, aby to naprawić później” == ”
Mając to na uwadze, jeśli rozmiar danych jest na tyle mały, że zmiany mogą być bardzo zapytane, i / lub masz okno konserwacji, które jest wystarczająco długie, aby nie tylko wprowadzić zmiany, ale także wycofać, jeśli coś pójdzie źle, wtedy normalizacja E
do tabeli pośredniej między tabelą klasy podstawowej a tabelami A
& B
podklasy mogłaby działać (choć nadal nie ma bezpośredniej wiedzy o konkretnym typie ( A
lubB
) w tabeli klasy podstawowej). ALE, jeśli masz w tych tabelach setki milionów wierszy i niewiarygodną ilość kodu odnoszącego się do tabel (kod, który należy przetestować po wprowadzeniu zmian), zwykle opłaca się być bardziej pragmatyczny niż idealistyczny. I z tym środowiskiem miałem do czynienia od lat: 987 milionów wierszy i 615 GB w tabeli klasy podstawowej, rozmieszczonych na 18 serwerach. Tyle tabel uderzyło w te tabele (tabele klasy podstawowej i podklasy), że napotkano duży opór - głównie ze strony kierownictwa, ale czasem reszty zespołu - na wprowadzanie jakichkolwiek zmian ze względu na stopień rozwoju i Zasoby związane z kontrolą jakości, które należałoby przydzielić.
Ponownie więc „najlepsze” podejście można ustalić tylko w zależności od sytuacji: musisz znać swój system (tj. Ile danych i jak odnoszą się wszystkie tabele i kod), jak dokonać refaktoryzacji i ludzi z którym współpracujesz (Twój zespół i ewentualnie kierownictwo - czy możesz uzyskać ich wpisowe na taki projekt?). Są pewne zmiany, o których wspominałem i planowałem przez 1–2 lata, i wziąłem wiele sprintów / wydań, aby wdrożyć może 85% z nich. Ale jeśli masz tylko <1 milion wierszy i nie ma dużo kodu powiązanego z tymi tabelami, prawdopodobnie możesz zacząć od bardziej idealnej / „czystej” strony.
Pamiętaj tylko, niezależnie od tego, którą drogą wybierzesz, zwróć uwagę na to, jak działa ten model przez co najmniej 2 lata (jeśli to możliwe). Zwróć uwagę na to, co zadziałało i co spowodowało ból, nawet jeśli wydawało się to najlepszym pomysłem w tamtym czasie (co oznacza, że musisz także pozwolić sobie na popsuwanie się - wszyscy tak robimy - abyś mógł uczciwie ocenić punkty bólu) ). I zwróć uwagę na to, dlaczego niektóre decyzje działały lub nie, abyś mógł podjąć decyzje, które prawdopodobnie będą „lepsze” następnym razem :-).
Zgodnie z moją interpretacją twoich specyfikacji chcesz znaleźć metodę implementacji dwóch różnych (ale połączonych ) struktur podtypów .
Aby objaśnić podejście do realizacji wspomnianego zadania, dodam do omawianego scenariusza dwa klasyczne typy hipotetycznych typów , które nazywamy
Foo
iBar
, które szczegółowo omówię poniżej.Zasady biznesowe
Oto kilka stwierdzeń, które pomogą mi stworzyć model logiczny:
A Foo is either one Bar or one C
A Foo is categorized by one FooType
A Bar is either one A or one C
A Bar is classified by one BarType
Model logiczny
Następnie wynikowy model logiczny IDEF1X [1] pokazano na rysunku 1 (można go również pobrać z Dropbox jako plik PDF ):
Dodatek Foo and Bar
Nie dodałem
Foo
iBar
żeby model wyglądał lepiej, ale żeby był bardziej wyrazisty. Uważam, że są one ważne z następujących powodów:Ponieważ
A
iB
dzielę się nazwanym atrybutemE
, ta funkcja sugeruje , że są to typy podobieństwa odrębnego (ale pokrewnego) rodzaju pojęcia , zdarzenia , osoby , miary itp., Które przedstawiłem za pomocąBar
typu podrzędności, który z kolei jest rodzaj podobieństwaFoo
, który posiadaD
atrybut na górze hierarchii.Ponieważ
C
dzieli tylko jeden atrybut z pozostałymi omawianymi typami bytu, tj.D
Aspekt ten sugeruje , że jest to rodzaj podobieństwa innego rodzaju koncepcji , zdarzenia , osoby , pomiaru itp., Dlatego przedstawiłem tę okoliczność na podstawieFoo
Super typu jednostki.Są to jednak tylko założenia, a ponieważ relacyjna baza danych ma dokładnie odzwierciedlać semantykę określonego kontekstu biznesowego , musisz zidentyfikować i sklasyfikować wszystkie interesujące rzeczy w Twojej konkretnej domenie, abyś mógł dokładnie uchwycić więcej znaczenia .
Ważne czynniki na etapie projektowania
Warto wiedzieć, że odkładając na bok całą terminologię, wyłączny klaster typu podtyp jest zwykłym związkiem. Opiszmy sytuację w następujący sposób:
Tak więc w tych przypadkach istnieje zgodność (lub liczność) jeden do jednego (1: 1).
Jak wiadomo z poprzednich postów, atrybut dyskryminujący (kolumna, gdy jest zaimplementowany) odgrywa nadrzędną rolę podczas tworzenia powiązania tego rodzaju, ponieważ wskazuje on poprawną instancję podtypu, z którą połączony jest nadtyp . migracja z klucz podstawowy z (i) supertypem do (ii) podtypy również pierwszorzędne znaczenie.
Betonowa struktura DDL
A potem napisałem strukturę DDL opartą na modelu logicznym przedstawionym powyżej:
Dzięki tej strukturze unikasz przechowywania znaków NULL w tabelach podstawowych (lub relacjach ), co wprowadziłoby niejednoznaczność do bazy danych.
Uczciwość, spójność i inne względy
Po wdrożeniu bazy danych należy upewnić się, że (a) każdy wyłączny wiersz nadtypu jest zawsze uzupełniany przez odpowiedni odpowiednik podtypu, a z kolei gwarantuje, że (b) taki wiersz podtypu jest zgodny z wartością zawartą w kolumnie dyskryminatora nadtypu . Dlatego bardzo wygodnie jest stosować ACID
TRANSACTIONS
, aby upewnić się, że warunki te są spełnione w bazie danych.Nie należy rezygnować z logicznej solidności, samoekspresji i dokładności bazy danych, są to aspekty, które zdecydowanie sprawiają, że baza danych jest bardziej solidna.
Dwie wcześniej opublikowane odpowiedzi już zawierają istotne punkty, które z pewnością warto wziąć pod uwagę przy projektowaniu, tworzeniu i zarządzaniu bazą danych i jej aplikacjami.
Pobieranie danych za pomocą definicji VIEW
Można skonfigurować niektóre widoki, które łączą kolumny różnych grup podtypów i typów , dzięki czemu można pobrać dostępne dane bez, np. Pisania za każdym razem niezbędnych klauzul JOIN. W ten sposób możesz z łatwością WYBIERAĆ bezpośrednio Z WIDOKU ( zależność pochodna lub tabela ).
Jak widać, „Ted” Codd był niewątpliwie genialny. Narzędzia, które zapisał, są dość mocne i eleganckie, i oczywiście są dobrze zintegrowane ze sobą.
Powiązane zasoby
Jeśli chcesz przeanalizować obszerną bazę danych, która obejmuje relacje nadtyp-podtyp, możesz znaleźć niezwykłe odpowiedzi zaproponowane przez @PerformanceDBA na następujące pytania dotyczące przepełnienia stosu:
Baza danych historycznych / podlegających kontroli .
[O] ne table czy wiele dla wielu różnych, ale interaktywnych wydarzeń? , który zawiera zagnieżdżone podtypy.
Uwaga
1. Definicja integracji dla modelowania informacji ( IDEF1X ) jest wysoce zalecaną techniką modelowania danych, która została ustanowiona jako standard w grudniu 1993 r. Przez Narodowy Instytut Norm i Technologii Stanów Zjednoczonych ( NIST ). Jest solidnie oparty na (a) wczesnym materiale teoretycznym autorstwa dr EF Codda; od (b) do związków encji świetle danych, opracowanych przez dr PP Chen ; a także w (c) Logical Database Design Technique, stworzonej przez Roberta G. Browna. Warto zauważyć, że IDEF1X został sformalizowany za pomocą logiki pierwszego rzędu.
źródło
E
całkowicie! Powodem przyjęcia odpowiedzi użytkownika srutzky jest to, że zapewnia on dobre punkty, które pomagają mi podjąć decyzję o wyborze najbardziej efektywnej trasy. Gdyby nie to, zaakceptowałbym twoją odpowiedź. Wcześniej głosowałem za odpowiedzią. Dzięki jeszcze raz!