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
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!
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
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:
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).
Erstellen Sie einen eindeutigen Index für jede Tabelle mit der PK der Tabelle und dem Feld für die Tabellen-ID.
Fügen Sie Ihrer Tabelle "Abzüge" ein winziges Feld hinzu, um die zweite Hälfte des Fremdschlüssels (die Tabellen-ID) zu speichern.
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:
ALTERTABLE[dbo].[Deductions]WITHNOCHECKADDCONSTRAINT[FK_Deductions_employees_ce]FOREIGNKEY([id],[fk_tid])REFERENCES[dbo].[employees_ce]([empid],[tid])NOTFORREPLICATION
GO
ALTERTABLE[dbo].[Deductions]NOCHECKCONSTRAINT[FK_600_WorkComments_employees_ce]
GO
ALTERTABLE[dbo].[Deductions]WITHNOCHECKADDCONSTRAINT[FK_Deductions_employees_sn]FOREIGNKEY([id],[fk_tid])REFERENCES[dbo].[employees_sn]([empid],[tid])NOTFORREPLICATION
GO
ALTERTABLE[dbo].[Deductions]NOCHECKCONSTRAINT[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 uniqueindex**
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.
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 .
Table1 Fruit
pk_fruitid, name
1, apple
2, pear
Table2 Meat
Pk_meatid, name
1, beef
2, chicken
Table3 Entity's
PK_entityid, anme
1, fruit
2, meat
3, desert
Table4 Basket (Tableusing fk_s)
PK_basketid, fk_entityid, pseudo_entityrow
1,2,2(Chicken - entity denotes meat table, pseudokey denotes rowin 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
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.
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).
deductions table
id parentId parentType name
11 ce gold
21 sn silver
32 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).
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).
type
in der Mitarbeitertabelle wärece
odersn
.quelle
LEFT JOIN
alle, 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 Spaltungemployees_ce
undemployees_sn
wurde angenommen, und diese Annahme wird notiert.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:
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).
Erstellen Sie einen eindeutigen Index für jede Tabelle mit der PK der Tabelle und dem Feld für die Tabellen-ID.
Fügen Sie Ihrer Tabelle "Abzüge" ein winziges Feld hinzu, um die zweite Hälfte des Fremdschlüssels (die Tabellen-ID) zu speichern.
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:
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:
Die Integrität wird nur überprüft, wenn die Spalte nicht null ist, sodass Sie die referenzielle Integrität beibehalten können.
quelle
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
DELETE
solchen in Ihrem Code befassen .Das Beispiel von SO Op würde so aussehen
quelle
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.
quelle
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).
quelle
Angenommen, Sie müssen aus irgendeinem Grund zwei Tabellen für die beiden Mitarbeitertypen haben, werde ich die Antwort von vmarquez erweitern:
Schema:
Daten in Abzügen:
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).
quelle