Projekt bazy danych: Dwa relacje 1 do wielu w tej samej tabeli

20

Muszę wymodelować sytuację, w której mam tabelę Chequing_Account (która zawiera budżet, numer iban i inne szczegóły konta), która musi być powiązana z dwiema różnymi tabelami Osoba i Korporacja, które mogą mieć 0, 1 lub wiele kont sprawdzających.

Innymi słowy, mam dwa relacje 1 do wielu z tym samym kontem Chequing w tabeli

Chciałbym usłyszeć rozwiązania tego problemu, które spełniają wymogi normalizacji. Większość rozwiązań, o których słyszałem, to:

1) znajdź wspólny podmiot, do którego należą zarówno Osoba, jak i Korporacja, i utwórz tabelę łączy między tą tabelą a tabelą Chequing_Account, w moim przypadku nie jest to możliwe, a nawet gdyby tak było, chcę rozwiązać ogólny problem, a nie tę konkretną instancję.

2) Utwórz dwie tabele łączy PersonToChequingAccount i CorporationToChequingAccount, które łączą te dwa podmioty z Kontami Chequing. Jednak nie chcę, aby dwie osoby miały to samo konto sprawdzające, i nie chcę, aby osoba fizyczna i korporacja dzieliły się kontem sprawdzającym! zobacz ten obraz

http://i41.tinypic.com/35i6kbk.png

3) Utwórz dwa klucze obce na koncie Chequing, które wskazują korporację i osobę fizyczną, jednak wymusiłbym w ten sposób, że osoba i firma mogą mieć wiele kont sprawdzania, jednak musiałbym ręcznie upewnić się, że dla każdego wiersza ChequingAccount nie oba relacje wskazują Korporacja i osoba fizyczna, ponieważ rachunek kontrolny jest albo korporacją, albo osobą fizyczną. zobacz ten obraz

http://i40.tinypic.com/1rpv9z.png

Czy jest jakieś inne czystsze rozwiązanie tego problemu?

dendini
źródło
Czy zastanawiałeś się nad np. OwnerTypeIDPrzy ChecquingAccountstole z, 1=Corporationi 2=NaturalPerson? W ten sposób potrzebujesz tylko jednego OwnerIDw ChecquingAccounttabeli, który możesz indeksować wraz z OwnerTypeID.
RoKa
Muszę nie tylko wiedzieć, czy jest to korporacja czy osoba fizyczna, ale także znać odpowiedni identyfikator, więc potrzebuję numeru identyfikacyjnego, a nie tylko wartości 1 lub 2! Rozwiązaniem 3 jest to, co znalazłem tutaj. Mam dwie kolumny z identyfikatorami korporacji lub osoby fizycznej
dendini
2
Tak, rozwiązanie jest prawidłową opcją. W większości DBMS możesz wymusić, że tylko jeden z dwóch FK jest „aktywny” z ograniczeniem sprawdzania: CHECK (CorporationID IS NOT NULL AND NaturalPersonID IS NULL OR CorporationID IS NULL AND NaturalPersonID IS NOT NULL)zdecydowanie wolę rozwiązanie 1 (ale to tylko ja). Jest o wiele „czystszy”.
ypercubeᵀᴹ
Tak, rozumiem, ale możesz mieć w ChecquingAccounttabeli zapis OwnerTypeID=1i OwnerID=123, wskazując, że jest to typ Corporation, a zatem identyfikator 123w Corporationtabeli. OwnerTypeID powie Ci, która tabela, a OwnerID powie Ci ID w tej tabeli.
RoKa
1
W jaki sposób opcja nr 1 jest niemożliwa? W końcu słowo „korporacja” zasadniczo oznacza „firmę, która zgodnie z prawem jest osobą”. Nazwij to Customersstołem.
Jon of All Trades

Odpowiedzi:

15

Relacyjne bazy danych nie są budowane, aby doskonale poradzić sobie z tą sytuacją. Musisz zdecydować, co jest dla Ciebie najważniejsze, a następnie dokonać kompromisu. Masz kilka celów:

  • Zachowaj trzecią normalną formę
  • Zachowaj integralność referencyjną
  • Zachowaj ograniczenie, że każde konto należy do korporacji lub osoby fizycznej.
  • Zachowaj możliwość łatwego i bezpośredniego pobierania danych

Problem polega na tym, że niektóre z tych celów konkurują ze sobą.

Rozwiązanie do podtypowania
Możesz wybrać rozwiązanie do podtytułu, w którym tworzysz supertyp, który obejmuje zarówno korporacje, jak i osoby. Ten nadtyp prawdopodobnie miałby klucz złożony z klucza naturalnego podtypu plus atrybut partycjonowania (np customer_type.). Jest to w porządku, o ile chodzi o normalizację, i pozwala egzekwować integralność referencyjną, a także ograniczenie wzajemnego wykluczania się korporacji i osób. Problem polega na tym, że utrudnia to pobieranie danych, ponieważ zawsze musisz rozgałęzić się na podstawie customer_typemomentu dołączenia konta do właściciela konta. Prawdopodobnie oznacza to używanie UNIONi powtarzanie zapytania w zapytaniu.

Rozwiązanie z
dwoma kluczami obcymi Możesz wybrać rozwiązanie, w którym przechowujesz dwa klucze obce w tabeli rachunków, jeden dla korporacji i jeden dla osoby. To rozwiązanie pozwala również zachować integralność referencyjną, normalizację i wzajemną wyłączność. Ma również tę samą wadę pobierania danych, co rozwiązanie do podtypów. W rzeczywistości to rozwiązanie jest podobne do podtytułu, z tą różnicą, że masz problem z rozgałęzieniem logiki łączenia „wcześniej”.

Niemniej jednak wielu modelarzy danych uznałoby to rozwiązanie za gorsze od rozwiązania podtypów z uwagi na sposób egzekwowania ograniczenia wzajemnej wyłączności. W rozwiązaniu do podtypowania tekstu używasz kluczy do egzekwowania wzajemnej wyłączności. W dwóch rozwiązaniach z kluczem obcym używasz CHECKograniczenia. Znam niektórych ludzi, którzy mają nieuzasadnione uprzedzenia wobec ograniczeń czekowych. Ci ludzie wolą rozwiązanie, które utrzymuje ograniczenia w kluczach.

Rozwiązanie „zdenormalizowanego” atrybutu partycjonowania
Istnieje jeszcze jedna opcja, w której trzymasz jedną kolumnę klucza obcego w tabeli kont sprawdzających i używasz innej kolumny, aby powiedzieć ci, jak interpretować kolumnę klucza obcego (RoKa'sOwnerTypeIDkolumna). To zasadniczo eliminuje tabelę supertypu w rozwiązaniu podtypowania przez denormalizowanie atrybutu partycjonowania do tabeli potomnej. (Należy zauważyć, że zgodnie z formalną definicją nie jest to ściśle „denormalizacja”, ponieważ atrybut partycjonowania jest częścią klucza podstawowego.) To rozwiązanie wydaje się dość proste, ponieważ unika się konieczności posiadania dodatkowej tabeli w celu wykonania mniej więcej tej samej rzeczy i to zmniejsza liczbę kolumn klucza obcego do jednego. Problem z tym rozwiązaniem polega na tym, że nie unika rozgałęzienia logiki pobierania, a ponadto nie pozwala zachować deklaratywnej integralności referencyjnej. Bazy danych SQL nie mają możliwości zarządzania jedną kolumną klucza obcego dla jednej z wielu tabel nadrzędnych.

Wspólne rozwiązanie domeny klucza podstawowego
Jednym ze sposobów, w jaki ludzie czasami radzą sobie z tym problemem, jest użycie pojedynczej puli identyfikatorów, aby nie było pomyłki dla dowolnego identyfikatora, niezależnie od tego, czy należy on do jednego podtypu, czy innego. Prawdopodobnie działałoby to całkiem naturalnie w scenariuszu bankowym, ponieważ nie zamierzasz wydawać tego samego numeru konta bankowego zarówno korporacji, jak i osobie fizycznej. Ma to tę zaletę, że pozwala uniknąć potrzeby atrybutu partycjonowania. Możesz to zrobić z tabelą supertypu lub bez niej. Korzystanie z tabeli typu super pozwala na stosowanie ograniczeń deklaratywnych w celu wymuszenia unikatowości. W przeciwnym razie musiałoby to być egzekwowane proceduralnie. To rozwiązanie jest znormalizowane, ale nie pozwoli ci zachować deklaratywnej integralności referencyjnej, chyba że utrzymasz tabelę supertypu. Nadal nic nie robi, aby uniknąć złożonej logiki pobierania.

Widać zatem, że tak naprawdę nie jest możliwe stworzenie przejrzystego projektu zgodnego ze wszystkimi zasadami, a jednocześnie prostego pobierania danych. Musisz zdecydować, gdzie będą twoje kompromisy.

Joel Brown
źródło
Moje rozwiązanie nr 2, do której z twoich czterech grup należy? „Denormalizowane rozwiązanie do partycjonowania atrybutów” nie jest dla mnie całkiem jasne.
dendini
@dendini - Twoje rozwiązanie nr 2 nie pasuje do żadnego z opisanych przeze mnie rozwiązań. Wynika to z faktu, że nie spełnia wymogu posiadania konta należącego do jednego podmiotu prawnego. Sposób, w jaki zdefiniowałeś klucze podstawowe tabel pośrednich, to przecięcia wiele do wielu. Jeśli klawisze pierwotne były tylko corporation_id i person_idwtedy można zasadniczo mamy rozwiązanie sub-pisania, poza tym, że stół super typ byłby podział na dwie, a klucz obcy zostaną odwrócone, więc ludzie nie mogą posiadać wiele kont. Ten rodzaj pokonuje cel.
Joel Brown
Świetne wyjaśnienie. @JoelBrown, jakie są implikacje wydajnościowe rozwiązania „Dwa klucze obce” pod względem zapytań? Biorąc pod uwagę, że zamiast 2, może być 6 lub więcej kluczy obcych: czy nadal poleciłbyś to podejście, czy raczej skłaniałbyś się w kierunku innego?
Amadeo Gallardo,
1
@AmadeoGallardo Odpowiedź brzmi „to zależy”. Zapytanie o klucz jest zawsze dość wydajne, ponieważ ogólnie możesz liczyć na skanowanie indeksu przynajmniej, jeśli nie wyszukiwanie, a są to szybkie operacje. Problem pojawia się, gdy przeszukujesz oba klucze w rozwiązaniu dwóch kluczy obcych . Tutaj pytasz optymalizatora zapytań o wykonanie operacji. W najlepszym wypadku podwoi to koszt zapytania, zwykle nieco gorzej, ponieważ musisz wykonać zapytanie do jednego klucza, a następnie drugiego, a następnie scalić wyniki.
Joel Brown,
@JoelBrown Denormalizowane przyszłe wersje SQL powinny zezwalać na to podejście, umożliwiając definicję złożonego klucza obcego na podstawie dwóch kolumn RefIDi RefTablegdzie RefTablejest ustalonym identyfikatorem identyfikującym tabelę docelową. Istnieje wiele przypadków użycia tego typu klucza i zbyt wiele, aby utrzymać 10 lub więcej tabel asocjacji / podtypów w celu wymuszenia integralności. Dla tych przypadków keysam to stworzyłem .
djmj