Clé étrangère faisant référence aux clés primaires sur plusieurs tables?

91

J'ai à deux tables à savoir employés_ce et employés_sn sous les employés de base de données.

Ils ont tous deux leurs colonnes de clé primaire uniques respectives.

J'ai une autre table appelée déductions, dont je veux faire référence à la colonne de clé étrangère pour les clés primaires de Employees_ce ainsi que Employees_sn. Est-ce possible?

par exemple

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

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

alors est-ce possible?

deductions
--------------
id      name
khce1   gold
khsn1   silver
Cœur
la source

Réponses:

98

En supposant que j'ai bien compris votre scénario, voici ce que j'appellerais la bonne façon de procéder:

Commencez par une description de plus haut niveau de votre base de données! Vous avez des employés, et les employés peuvent être des employés «ce» et des employés «sn» (quels qu'ils soient). En termes orientés objet, il existe une classe "employé", avec deux sous-classes appelées "employé ce" et "employé sn".

Ensuite , vous traduisez cette description de niveau supérieur à trois tables: employees, employees_ceet employees_sn:

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

Puisque tous les employés sont des employés (duh!), Chaque employé aura une ligne dans le employeestableau. Les employés "ce" ont également une ligne dans le employees_cetableau et les employés "sn" ont également une ligne dans le employees_sntableau. employees_ce.idest une clé étrangère de employees.id, telle employees_sn.idquelle.

Pour désigner un employé de quelque nature que ce soit (ce ou sn), reportez-vous au employeestableau. Autrement dit, la clé étrangère avec laquelle vous avez eu des problèmes doit faire référence à cette table!

Thomas Padron-McCarthy
la source
17
Comment rendre ce et sn mutuellement exclusifs? Étant donné qu'un employé ne peut pas être à la fois ce et sn, il serait bon de refléter cela dans la base de données. J'ai ce problème en ce moment.
Rolf le
Je pense que plusieurs clés de colonne pourraient aider à résoudre le problème dans mon commentaire précédent ... en recherchant cela maintenant.
Rolf le
12
Vous pouvez forcer l'employé à ne figurer que dans une seule table (et la bonne) en stockant un type dans la table de base ainsi que dans les tables dérivées. Définissez l'identifiant de la clé primaire, une clé unique sur (id, type), la clé étrangère des tables enfants sur (id, type) et placez une contrainte CHECK sur chaque table enfant pour n'avoir que le type correct. Ou, si votre base de données effectue des vérifications globales (et sans pénalité de vitesse énorme), vous pouvez bien sûr simplement faire une vérification NOT EXISTS.
derobert
Consultez cette réponse pour obtenir des explications complètes et des détails de mise en œuvre.
PerformanceDBA
comment savoir qu'un employé avec un identifiant spécifique est «se» ou «sn»?
mhrsalehi
22

Vous pouvez probablement ajouter deux contraintes de clé étrangère (honnêtement: je ne l'ai jamais essayé), mais cela insisterait alors pour que la ligne parent existe dans les deux tables.

Au lieu de cela, vous souhaitez probablement créer un supertype pour vos deux sous-types d'employés, puis pointer la clé étrangère à la place. (En supposant que vous ayez une bonne raison de séparer les deux types d'employés, bien sûr).

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

typedans la table des employés serait ceou sn.

derobert
la source
J'ai essayé d'ajouter plusieurs clés étrangères, elles ont fonctionné mais, en ajoutant un enregistrement, java derby me dit que les deux contraintes de clé étrangère ont été violées!
Je viens de l'essayer sur PostgreSQL, et cela fonctionne là-bas. Aviez-vous l'enregistrement parent dans les deux tables?
derobert
fiche parent vous voulez dire, l'empid?
Le problème a certainement été déplacé de la table des «retenues» à la table des «employés». Comment référeriez-vous des entités potentiellement différentes en fonction d'un type?
gawpertron
1
@gawpertron: Eh bien, l'empid est unique dans tous les types. Vous pouvez utiliser le champ «type» pour voir quelle sous-table vous devez référencer. Ou juste LEFT JOINtous, s'il y en a peu. Lorsque vous n'utilisez pas la table de base 'employee', la clé primaire n'a pas pu être déclarée (car elle fait référence à tableA ou tableB ou…); maintenant ça peut l'être. La sagesse du fractionnement employees_ceet a employees_snété supposée, et cette hypothèse est notée.
derobert
19

En fait, je fais ça moi-même. J'ai une table appelée «Commentaires» qui contient des commentaires pour les enregistrements dans 3 autres tables. Aucune des deux solutions ne gère réellement tout ce que vous voulez probablement. Dans votre cas, vous feriez ceci:

Solution 1:

  1. Ajoutez un champ tinyint à Employees_ce et Employees_sn qui a une valeur par défaut qui est différente dans chaque table (Ce champ représente un 'identificateur de table', nous les appellerons donc tid_ce & tid_sn)

  2. Créez un index unique sur chaque table en utilisant le PK de la table et le champ d'ID de table.

  3. Ajoutez un champ tinyint à votre table 'Deductions' pour stocker la seconde moitié de la clé étrangère (l'ID de table)

  4. Créez 2 clés étrangères dans votre table 'Deductions' (Vous ne pouvez pas appliquer l'intégrité référentielle, car une clé sera valide ou l'autre ... mais jamais les deux:

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

Solution 2: Cette solution permet de maintenir l'intégrité référentielle: 1. Créez un deuxième champ de clé étrangère dans la table 'Deductions', autorisez les valeurs Null dans les deux clés étrangères et créez des clés étrangères normales:

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

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

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

L'intégrité n'est vérifiée que si la colonne n'est pas nulle, vous pouvez donc conserver l'intégrité référentielle.

LittleC
la source
6

Je sais que c'est un sujet qui stagne depuis longtemps, mais au cas où quelqu'un chercherait, voici comment je gère les clés étrangères multi-tables. Avec cette technique, vous n'avez pas d'opérations en cascade appliquées par DBA, alors assurez-vous de traiter DELETEet autres dans votre code.

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)

L'exemple de SO Op ressemblerait à ceci

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

types
---------------------
1 employees_ce
2 employees_sn
Brian Sallee
la source
1

Techniquement possible. Vous référeriez probablement employés_ce dans les retenues et employés_sn. Mais pourquoi ne pas fusionner employés_sn et employés_ce? Je ne vois aucune raison pour laquelle vous avez deux tables. Personne à plusieurs relations. Et (pas dans cet exemple) de nombreuses colonnes.

Si vous faites deux références pour une colonne, un employé doit avoir une entrée dans les deux tables.

Sascha
la source
1

Oui c'est possible. Vous devrez définir 2 FK pour la 3e table. Chaque FK pointant vers le (s) champ (s) requis d'une table (soit 1 FK par table étrangère).

vmarquez
la source
0

En supposant que vous devez avoir deux tables pour les deux types d'employés pour une raison quelconque, je vais prolonger la réponse de vmarquez:

Schéma:

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

Données en déductions:

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

Cela vous permettrait de faire pointer les déductions vers n'importe quelle autre table de votre schéma. Ce type de relation n'est pas pris en charge par les contraintes au niveau de la base de données, IIRC donc vous devrez vous assurer que votre application gère correctement la contrainte (ce qui la rend plus lourde si vous avez plusieurs applications / services différents qui frappent la même base de données).

r00fus
la source