Klucz obcy odnoszący się do kluczy podstawowych w wielu tabelach?

92

Mam do dwóch tabel czyli workers_ce i workers_sn pod bazą danych workers.

Oba mają swoje unikalne kolumny klucza podstawowego.

Mam kolejną tabelę o nazwie deductions, której kolumna klucza obcego chcę odnieść do kluczy podstawowych workers_ce oraz workers_sn. czy to możliwe?

na przykład

employees_ce
--------------
empid   name
khce1   prince

employees_sn
----------------
empid   name
khsn1   princess

więc czy to możliwe?

deductions
--------------
id      name
khce1   gold
khsn1   silver
Cœur
źródło

Odpowiedzi:

99

Zakładając, że dobrze zrozumiałem twój scenariusz, nazwałbym właściwy sposób, aby to zrobić:

Zacznij od opisu swojej bazy danych na wyższym poziomie! Masz pracowników, a pracownicy mogą być pracownikami „ce” i pracownikami „sn” (cokolwiek to jest). W kategoriach obiektowych istnieje klasa „pracownik” z dwiema podklasami nazywanymi „pracownikiem ce” i „pracownikiem sn”.

Następnie przełożyć tego opisu wyższego poziomu do trzech tabelach: employees, employees_cei employees_sn:

  • employees(id, name)
  • employees_ce(id, ce-specific stuff)
  • employees_sn(id, sn-specific stuff)

Ponieważ wszyscy pracownicy są pracownikami (prawda!), Każdy pracownik będzie miał wiersz w employeestabeli. Pracownicy „ce” również mają wiersz w employees_cetabeli, a pracownicy „sn” również mają wiersz w employees_sntabeli. employees_ce.idjest kluczem obcym do employees.id, tak jak employees_sn.idjest.

Aby odnieść się do pracownika dowolnego rodzaju (ce lub sn), zapoznaj się z employeestabelą. Oznacza to, że klucz obcy, z którym miałeś problem, powinien odnosić się do tej tabeli!

Thomas Padron-McCarthy
źródło
17
Jak sprawić, by ce i sn wzajemnie się wykluczały? Ponieważ pracownik nie może być jednocześnie ce i sn, dobrą praktyką byłoby odzwierciedlenie tego w bazie danych. Mam teraz ten problem.
Rolf
Myślę, że wiele klawiszy kolumn może pomóc w rozwiązaniu problemu z moim poprzednim komentarzem ... szukając tego teraz.
Rolf
12
Możesz zmusić pracownika do przebywania tylko w jednej tabeli (i właściwej), przechowując typ w tabeli bazowej, a także tabelach pochodnych. Utwórz identyfikator klucza podstawowego, unikatowy klucz on (id, typ), klucz obcy tabel podrzędnych będzie włączony (id, typ) i umieść ograniczenie CHECK na każdej tabeli podrzędnej, aby mieć tylko poprawny typ. Lub, jeśli Twoja baza danych wykonuje globalne ograniczenia sprawdzające (i bez dużej kary za prędkość), możesz oczywiście po prostu wykonać sprawdzenie NIE ISTNIEJE.
derobert
Sprawdź tę odpowiedź, aby uzyskać pełne wyjaśnienie i szczegóły implementacji.
PerformanceDBA
po czym poznać pracownika z określonym identyfikatorem to „se” lub „sn”?
mhrsalehi
22

Prawdopodobnie możesz dodać dwa ograniczenia klucza obcego (szczerze: nigdy tego nie próbowałem), ale wtedy nalegałby, aby wiersz nadrzędny istniał w obu tabelach.

Zamiast tego prawdopodobnie chcesz utworzyć nadtyp dla dwóch podtypów pracowników, a następnie wskazać tam klucz obcy. (Zakładając oczywiście, że masz dobry powód, aby podzielić te dwa typy pracowników).

                 employee       
employees_ce     ————————       employees_sn
————————————     type           ————————————
empid —————————> empid <——————— empid
name               /|\          name
                    |  
                    |  
      deductions    |  
      ——————————    |  
      empid ————————+  
      name

typew tabeli pracowników byłoby celub sn.

derobert
źródło
Próbowałem dodać wiele kluczy obcych, działały, ale podczas dodawania rekordu java Derby informuje mnie, że oba ograniczenia kluczy obcych zostały naruszone!
Właśnie wypróbowałem to na PostgreSQL i tam działa. Czy miałeś rekord rodzica w obu tabelach?
derobert
rekord rodziców masz na myśli, empid?
Z pewnością problem został przeniesiony z tabeli „odliczeń” do tabeli „pracownik”. Jak odnosisz się do potencjalnie różnych jednostek na podstawie typu?
gawpertron,
1
@gawpertron: Cóż, empid jest wyjątkowy we wszystkich typach. Możesz użyć pola „typ”, aby zobaczyć, do której podtabeli należy się odwołać. Lub po prostu LEFT JOINwszystkie, jeśli jest ich mało. Gdy nie używasz tabeli bazowej „pracownika”, nie można zadeklarować klucza podstawowego (ponieważ odwołuje się on do tabeli A lub tabeli B lub…); teraz może być. Mądrość rozszczepienia employees_cei employees_snzostała przyjęta, a założenie to zostało odnotowane.
derobert
20

Właściwie robię to sam. Mam tabelę o nazwie „Komentarze”, która zawiera komentarze do rekordów w 3 innych tabelach. Żadne rozwiązanie nie obsługuje w rzeczywistości wszystkiego, czego prawdopodobnie chcesz. W twoim przypadku zrobiłbyś to:

Rozwiązanie 1:

  1. Dodaj pole tinyint do workers_ce i workers_sn, które ma domyślną wartość, która jest inna w każdej tabeli (to pole reprezentuje `` identyfikator tabeli '', więc nazwiemy je tid_ce & tid_sn)

  2. Utwórz unikalny indeks dla każdej tabeli, używając PK tabeli i pola identyfikatora tabeli.

  3. Dodaj pole tinyint do tabeli „Deductions”, aby przechowywać drugą połowę klucza obcego (identyfikator tabeli)

  4. Utwórz 2 klucze obce w swojej tabeli „Odliczenia” (nie możesz wymusić integralności referencyjnej, ponieważ jeden klucz będzie ważny lub drugi ... ale nigdy oba:

    ALTER TABLE [dbo].[Deductions]  WITH NOCHECK ADD  CONSTRAINT [FK_Deductions_employees_ce] FOREIGN KEY([id], [fk_tid])
    REFERENCES [dbo].[employees_ce] ([empid], [tid])
    NOT FOR REPLICATION 
    GO
    ALTER TABLE [dbo].[Deductions] NOCHECK CONSTRAINT [FK_600_WorkComments_employees_ce]
    GO
    ALTER TABLE [dbo].[Deductions]  WITH NOCHECK ADD  CONSTRAINT [FK_Deductions_employees_sn] FOREIGN KEY([id], [fk_tid])
    REFERENCES [dbo].[employees_sn] ([empid], [tid])
    NOT FOR REPLICATION 
    GO
    ALTER TABLE [dbo].[Deductions] NOCHECK CONSTRAINT [FK_600_WorkComments_employees_sn]
    GO
    
    employees_ce
    --------------
    empid    name     tid
    khce1   prince    1
    
    employees_sn
    ----------------
    empid    name     tid 
    khsn1   princess  2
    
    deductions
    ----------------------
    id      tid       name  
    khce1   1         gold
    khsn1   2         silver         
    ** id + tid creates a unique index **
    

Rozwiązanie 2: To rozwiązanie pozwala zachować referencyjną integralność: 1. Utwórz drugie pole klucza obcego w tabeli „Odliczenia”, zezwól na wartości Null w obu kluczach obcych i utwórz normalne klucze obce:

    employees_ce
    --------------
    empid   name
    khce1   prince 

    employees_sn
    ----------------
    empid   name     
    khsn1   princess 

    deductions
    ----------------------
    idce    idsn      name  
    khce1   *NULL*    gold
    *NULL*  khsn1     silver         

Integralność jest sprawdzana tylko wtedy, gdy kolumna nie jest pusta, więc można zachować integralność referencyjną.

LittleC
źródło
6

Wiem, że to od dawna zastój temat, ale na wypadek, gdyby ktoś szukał tutaj, to jak radzę sobie z wielotabelowymi kluczami obcymi. Dzięki tej technice nie masz żadnych wymuszonych przez DBA operacji kaskadowych, więc upewnij się, że zajmujesz się nimi DELETEw swoim kodzie.

Table 1 Fruit
pk_fruitid, name
1, apple
2, pear

Table 2 Meat
Pk_meatid, name
1, beef
2, chicken

Table 3 Entity's
PK_entityid, anme
1, fruit
2, meat
3, desert

Table 4 Basket (Table using fk_s)
PK_basketid, fk_entityid, pseudo_entityrow
1, 2, 2 (Chicken - entity denotes meat table, pseudokey denotes row in indictaed table)
2, 1, 1 (Apple)
3, 1, 2 (pear)
4, 3, 1 (cheesecake)

Przykład SO Op wyglądałby tak

deductions
--------------
type    id      name
1      khce1   gold
2      khsn1   silver

types
---------------------
1 employees_ce
2 employees_sn
Brian Sallee
źródło
1

Technicznie możliwe. Prawdopodobnie odnosiłbyś się do workers_ce w odliczeniach i workers_sn. Ale dlaczego nie połączysz workers_sn i workers_ce? Nie widzę powodu, dla którego masz dwa stoliki. Nikt do wielu relacji. I (nie w tym przykładzie) wiele kolumn.

Jeśli robisz dwa odwołania dla jednej kolumny, pracownik musi mieć wpis w obu tabelach.

Sascha
źródło
1

Tak to mozliwe. Będziesz musiał zdefiniować 2 FK dla 3. tabeli. Każdy FK wskazujący wymagane pola jednej tabeli (tj. 1 FK na tabelę zagraniczną).

vmarquez
źródło
0

Zakładając, że z jakiegoś powodu musisz mieć dwie tabele dla dwóch typów pracowników, rozszerzę odpowiedź vmarquez:

Schemat:

employees_ce (id, name)
employees_sn (id, name)
deductions (id, parentId, parentType, name)

Dane w potrąceniach:

deductions table
id      parentId      parentType      name
1       1             ce              gold
2       1             sn              silver
3       2             sn              wood
...

Umożliwiłoby to odliczenie wskazujące na dowolną inną tabelę w schemacie. Ten rodzaj relacji nie jest obsługiwany przez ograniczenia na poziomie bazy danych, IIRC, więc musisz upewnić się, że Twoja aplikacja prawidłowo zarządza ograniczeniem (co czyni ją bardziej uciążliwą, jeśli masz kilka różnych aplikacji / usług trafiających do tej samej bazy danych).

r00fus
źródło