Fremdschlüssel, der sich auf Primärschlüssel in mehreren Tabellen bezieht?

91

Ich habe zwei Tabellen, nämlich employee_ce und employee_sn unter der Datenbank employee.

Beide haben ihre jeweiligen eindeutigen Primärschlüsselspalten.

Ich habe eine andere Tabelle namens Abzüge, deren Fremdschlüsselspalte ich auf die Primärschlüssel von employee_ce sowie employee_sn verweisen möchte. Ist das möglich?

beispielsweise

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

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

ist das also möglich?

deductions
--------------
id      name
khce1   gold
khsn1   silver
Cœur
quelle

Antworten:

98

Unter der Annahme, dass ich Ihr Szenario richtig verstanden habe, würde ich dies als den richtigen Weg bezeichnen:

Beginnen Sie mit einer übergeordneten Beschreibung Ihrer Datenbank! Sie haben Mitarbeiter, und Mitarbeiter können "ce" -Mitarbeiter und "sn" -Mitarbeiter sein (was auch immer diese sind). Objektorientiert gibt es eine Klasse "Mitarbeiter" mit zwei Unterklassen "ce employee" und "sn employee".

Anschließend übersetzen Sie diese übergeordnete Beschreibung in drei Tabellen : employees, employees_ceund employees_sn:

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

Da alle Mitarbeiter Mitarbeiter sind (duh!), Wird jeder Mitarbeiter eine Zeile in der employeesTabelle haben. "ce" -Mitarbeiter haben auch eine Zeile in der employees_ceTabelle, und "sn" -Mitarbeiter haben auch eine Zeile in der employees_snTabelle. employees_ce.idist ein Fremdschlüssel für employees.id, so wie es employees_sn.idist.

Informationen zum Verweisen auf einen Mitarbeiter jeglicher Art (ce oder sn) finden Sie in der employeesTabelle. Das heißt, der Fremdschlüssel, mit dem Sie Probleme hatten, sollte sich auf diese Tabelle beziehen!

Thomas Padron-McCarthy
quelle
17
Wie schließen sich ce und sn gegenseitig aus? Da ein Mitarbeiter nicht gleichzeitig ce und sn sein kann, ist es empfehlenswert, dies in der Datenbank wiederzugeben. Ich habe gerade dieses Problem.
Rolf
Ich denke, mehrere Spaltenschlüssel könnten bei dem Problem in meinem vorherigen Kommentar helfen ... das jetzt nachschlagen.
Rolf
12
Sie können den Mitarbeiter zwingen, sich nur in einer der Tabellen (und der richtigen) zu befinden, indem Sie einen Typ in der Basistabelle sowie in den abgeleiteten Tabellen speichern. Stellen Sie die Primärschlüssel-ID, einen eindeutigen Schlüssel (ID, Typ) und den Fremdschlüssel der untergeordneten Tabellen ein (ID, Typ), und legen Sie für jede untergeordnete Tabelle eine CHECK-Einschränkung fest, um nur den richtigen Typ zu haben. Wenn Ihre Datenbank globale Überprüfungsbeschränkungen durchführt (und dies ohne große Geschwindigkeitseinbußen), können Sie natürlich auch nur eine NOT EXISTS-Überprüfung durchführen.
Derobert
Überprüfen Sie diese Antwort auf vollständige Erklärungen und Implementierungsdetails.
PerformanceDBA
Woher weiß man, dass ein Mitarbeiter mit einer bestimmten ID "se" oder "sn" ist?
Mhrsalehi
22

Sie können wahrscheinlich zwei Fremdschlüsseleinschränkungen hinzufügen (ehrlich: Ich habe es noch nie versucht), aber es würde dann darauf bestehen, dass die übergeordnete Zeile in beiden Tabellen vorhanden ist.

Stattdessen möchten Sie wahrscheinlich einen Supertyp für Ihre beiden Mitarbeiter-Subtypen erstellen und stattdessen den Fremdschlüssel dort platzieren. (Vorausgesetzt, Sie haben einen guten Grund, die beiden Arten von Mitarbeitern zu trennen, natürlich).

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

typein der Mitarbeitertabelle wäre ceoder sn.

derobert
quelle
Ich habe versucht, mehrere Fremdschlüssel hinzuzufügen, sie haben funktioniert, aber beim Hinzufügen eines Datensatzes teilt mir Java Derby mit, dass beide Fremdschlüsseleinschränkungen verletzt wurden!
Ich habe es gerade mit PostgreSQL versucht und es funktioniert dort. Hatten Sie den übergeordneten Datensatz in beiden Tabellen?
Derobert
Elterndatensatz, den Sie sagen wollen, der Empid?
Sicherlich wurde das Problem von der Tabelle "Abzüge" auf die Tabelle "Mitarbeiter" verschoben. Wie würden Sie potenziell unterschiedliche Entitäten basierend auf einem Typ referenzieren?
Gawpertron
1
@gawpertron: Nun, das Empid ist für alle Typen einzigartig. Sie können das Feld 'Typ' verwenden, um zu sehen, auf welche Untertabelle Sie verweisen müssen. Oder einfach LEFT JOINalle, wenn es nur wenige gibt. Wenn die Basistabelle "Mitarbeiter" nicht verwendet wird, konnte der Primärschlüssel nicht deklariert werden (da er auf Tabelle A oder Tabelle B oder ... verweist). jetzt kann es sein. Die Weisheit der Spaltung employees_ceund employees_snwurde angenommen, und diese Annahme wird notiert.
Derobert
19

Eigentlich mache ich das selbst. Ich habe eine Tabelle namens 'Kommentare', die Kommentare für Datensätze in 3 anderen Tabellen enthält. Keine der beiden Lösungen behandelt tatsächlich alles, was Sie wahrscheinlich wollen. In Ihrem Fall würden Sie Folgendes tun:

Lösung 1:

  1. Fügen Sie employee_ce und employee_sn ein tinyint-Feld hinzu, dessen Standardwert in jeder Tabelle unterschiedlich ist (Dieses Feld stellt eine 'Tabellenkennung' dar, daher nennen wir sie tid_ce & tid_sn).

  2. Erstellen Sie einen eindeutigen Index für jede Tabelle mit der PK der Tabelle und dem Feld für die Tabellen-ID.

  3. Fügen Sie Ihrer Tabelle "Abzüge" ein winziges Feld hinzu, um die zweite Hälfte des Fremdschlüssels (die Tabellen-ID) zu speichern.

  4. Erstellen Sie zwei Fremdschlüssel in Ihrer Tabelle "Abzüge" (Sie können die referenzielle Integrität nicht erzwingen, da entweder der eine oder der andere Schlüssel gültig ist ... aber niemals beide:

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

Lösung 2: Mit dieser Lösung kann die referenzielle Integrität aufrechterhalten werden: 1. Erstellen Sie ein zweites Fremdschlüsselfeld in der Tabelle 'Abzüge', lassen Sie Nullwerte in beiden Fremdschlüsseln zu und erstellen Sie normale Fremdschlüssel:

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

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

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

Die Integrität wird nur überprüft, wenn die Spalte nicht null ist, sodass Sie die referenzielle Integrität beibehalten können.

LittleC
quelle
6

Ich weiß, dass dies ein lange stagnierendes Thema ist, aber falls jemand hier sucht, gehe ich mit Fremdschlüsseln mit mehreren Tabellen um. Mit dieser Technik haben Sie keine DBA-erzwungenen Kaskadenoperationen. Stellen Sie daher sicher, dass Sie sich mit DELETEsolchen in Ihrem Code befassen .

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)

Das Beispiel von SO Op würde so aussehen

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

types
---------------------
1 employees_ce
2 employees_sn
Brian Sallee
quelle
1

Technisch möglich. Sie würden wahrscheinlich auf employee_ce in Abzügen und employee_sn verweisen. Aber warum verschmelzen Sie nicht employee_sn und employee_ce? Ich sehe keinen Grund, warum Sie zwei Tische haben. Niemand zu viele Beziehung. Und (nicht in diesem Beispiel) viele Spalten.

Wenn Sie zwei Referenzen für eine Spalte erstellen, muss ein Mitarbeiter einen Eintrag in beiden Tabellen haben.

Sascha
quelle
1

Ja, es ist möglich. Sie müssen 2 FKs für die 3. Tabelle definieren. Jeder FK zeigt auf die erforderlichen Felder einer Tabelle (dh 1 FK pro Fremdtabelle).

vmarquez
quelle
0

Angenommen, Sie müssen aus irgendeinem Grund zwei Tabellen für die beiden Mitarbeitertypen haben, werde ich die Antwort von vmarquez erweitern:

Schema:

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

Daten in Abzügen:

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

Auf diese Weise können Abzüge auf eine andere Tabelle in Ihrem Schema verweisen. Diese Art von Beziehung wird nicht durch Einschränkungen auf Datenbankebene (IIRC) unterstützt. Sie müssen daher sicherstellen, dass Ihre App die Einschränkung ordnungsgemäß verwaltet (was es umständlicher macht, wenn mehrere verschiedene Apps / Dienste auf dieselbe Datenbank zugreifen).

r00fus
quelle