Czy powinienem dodać przechodnie klucze obce?

11

Prosty przykład: istnieje tabela klientów.

create table Customers (
  id integer,
  constraint CustomersPK primary key (id)
)

Wszystkie inne dane w bazie danych powinny zawierać linki do Customer, więc np. OrdersWygląda to tak:

create table Orders (
  id integer,
  customer integer,
  constraint OrdersPK primary key (customer, id),
  constraint OrdersFKCustomers foreign key (customer) references Customers (id)
)

Załóżmy, że teraz istnieje tabela prowadząca do Orders:

create table Items (
  id integer,
  customer integer,
  order integer,
  constraint ItemsPK primary key (customer, id),
  constraint ItemsFKOrders foreign key (customer, order) references Orders (customer, id)
)

Czy powinienem dodać osobny klucz obcy od Itemsdo Customers?

...
constraint ItemsFKCustomers foreign key (customer) references Customers (id)

Zamiast tego obraz: czy powinienem dodać linię przerywaną / FK?

Prosty przykładowy schemat


Edycja: Dodałem definicje klucza podstawowego do tabel. Chciałbym powtórzyć powyższy punkt: baza danych jest zasadniczo wyciszana przez klientów, jako środek poprawności / bezpieczeństwa. Dlatego wszystkie klucze podstawowe zawierają customeridentyfikator.

vektor
źródło
2
Nie powinieneś. Dodatkowy FK nie jest potrzebny. Ograniczenie jest egzekwowane przez pozostałe dwa FK.
ypercubeᵀᴹ
@ypercube Czy są jakieś kary za wyniki związane z posiadaniem redundantnego FK? Jakieś zalety, o których mógłbyś pomyśleć? ...
vektor
1
@vektor, aspekty wydajności prawdopodobnie różnią się w zależności od rdbms, ale ogólnie wpływ na wydajność ma każdy dodany FK, ponieważ każde wstawienie / aktualizacja / usunięcie w jednej z tabel PK / FK musi być sprawdzone pod kątem przymus. Przy dużych stołach PK ten spadek wydajności może być dość poważny.
Daniel Hutmacher

Odpowiedzi:

6

Myślę, że to jest oryginalny pomysł.

wprowadź opis zdjęcia tutaj

Pierwszą rzeczą, na którą należy zwrócić uwagę, jest fakt, że PK w tabeli LineItem ma trzy atrybuty {CustomerID, CustomerOrderNo, OdrerItemNo}, a nie tylko dwa w twoim przykładzie.

Drugą rzeczą do odnotowania jest zamieszanie wynikające z użycia idnazwy ogólnej dla atrybutu.

CustomerOrderNoPowinien być idealnie (1,2,3 ..) dla każdego klienta i OrderItemNo(1,2,3 ...) dla każdego zamówienia.

Cóż, jest to miłe, jeśli to możliwe, ale wymaga zapytania szukającego poprzedniej maksymalnej wartości, na przykład

select max(CustomerOrderNo)
from Order 
where CustomerID = specific_customer ; 

co często nie jest preferowane w środowiskach o dużej liczbie transakcji, więc często zdarza się, że zastępuje się je automatycznym przyrostem, co zasadniczo służy temu samemu celowi. To prawda, że ​​ten auto-incremet jest teraz unikalny, dlatego może być używany jako KLUCZ - ale możesz zdecydować się na to, by spojrzeć na to jako konieczny kompromis dla OrderItemNo.

Tak więc po zmianie nazwy CustomerOrderNo -> OrderNoi OrderItemNo-> ItemNomożesz dotrzeć do tego modelu

wprowadź opis zdjęcia tutaj

Więc teraz, jeśli spojrzysz na Orderponiższe, są wyjątkowe

{OrderNo}             -- PK
{CustomerID, OrderNo} -- superkey,  AK on the diagram.

Zauważ, że {CustomerID, OrderNo}jest propagowany do, LineItemaby służył jako FK.

Jeśli trochę PKs {ItemNo} and {OrderNo}zmrużysz oczy, jest to bliskie twojemu przykładowi, ale tylko z - w przeciwieństwie do dwóch kolumn PK z twojego przykładu.

Teraz pytanie brzmi: dlaczego nie uprościć czegoś takiego?

wprowadź opis zdjęcia tutaj

Co jest w porządku, ale wprowadza PATH ZALEŻNOŚĆ - nie można przyłączyć LineItemsię Customerbezpośrednio, należy użyć Orderw sprzężeniu.


Jeśli to możliwe, wolę pierwszy przypadek - wybierasz swój ulubiony. I oczywiście nie ma potrzeby bezpośredniego FK od LineItemdo Customerw tych trzech przypadkach.

Damir Sudarevic
źródło
Rozejrzałem się dookoła, ale nie widzę, by „zależność od ścieżki” była powszechnie stosowana w odniesieniu do tego, co nazwałbym „relacją przechodnią drugiego stopnia” (choć moja terminologia też prawdopodobnie jest nieprawidłowa)
Dai,
2

„Produkt” nie powinien bezpośrednio odwoływać się do „klienta”, ponieważ wynika to z „zamówienia” produktu. Tak więc kolumny „klient” nie będą w ogóle potrzebne.

Stosunek towaru do klienta jest zapewniony za pomocą istniejącego klucza obcego.

Jeśli order.id jest kolumną tożsamości, rozważ usunięcie item.customer razem.

Daniel Hutmacher
źródło
1
Dzięki, nie zauważyłem, że „klient” był również zawarty w pierwszym FK od „przedmiotów” do „zamówień”. Odpowiednio opracowałem swoją odpowiedź.
Daniel Hutmacher,
@DanielHutmacher Zredagowałem pytanie, aby zawierało podstawowe klucze moich tabel. To wyjaśnia dziwne FK, o których wspomniałeś w swojej edycji.
vektor
Ok, zaktualizowałem swoją odpowiedź. :)
Daniel Hutmacher
Zgaduję, że posiadanie customerwe wszystkich tabelach (a więc silosowanie DB) jest niezwykłym podejściem. Muszę wyznać, że to coś, co widziałem w mojej poprzedniej pracy. Czy to ma dla ciebie jakiś sens? Czy widziałeś już taki projekt?
vektor
Powiedziałbym, że wygląda na podejście do magazynu danych (schemat gwiazdy), w którym chcesz celowo zdenormalizować dane w celu wyeliminowania złączeń. Lub klucz podstawowy może być złożony, tj. Pierwsze, drugie, trzecie zamówienie klienta A, pierwsze, drugie zamówienie klienta B itp. - gdy sama kolumna identyfikatora zamówienia nie jest unikalna.
Daniel Hutmacher,