Chiave esterna che si riferisce a chiavi primarie su più tabelle?


91

Ho due tabelle, ovvero dipendenti_ce e dipendenti_sn sotto il database dipendenti.

Entrambi hanno le rispettive colonne chiave primaria univoche.

Ho un'altra tabella chiamata deduzioni, la cui colonna chiave esterna desidero fare riferimento alle chiavi primarie di dipendenti_ce e dipendenti_sn. È possibile?

per esempio

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

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

quindi è possibile?

deductions
--------------
id      name
khce1   gold
khsn1   silver

Risposte:


98

Supponendo di aver compreso correttamente il tuo scenario, questo è ciò che chiamerei il modo giusto per farlo:

Inizia da una descrizione di livello superiore del tuo database! Hai dipendenti e i dipendenti possono essere dipendenti "ce" e dipendenti "sn" (qualunque essi siano). In termini orientati agli oggetti, esiste una classe "dipendente", con due sottoclassi chiamate "ce dipendente" e "sn dipendente".

Poi si Traduci questa descrizione di livello superiore a tre tabelle: employees, employees_cee employees_sn:

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

Poiché tutti i dipendenti sono dipendenti (duh!), Ogni dipendente avrà una riga nella employeestabella. Anche i dipendenti "ce" hanno una riga nella employees_cetabella e anche i dipendenti "sn" hanno una riga nella employees_sntabella. employees_ce.idè una chiave esterna per employees.id, così com'è employees_sn.id.

Per fare riferimento a un dipendente di qualsiasi genere (ce o sn) fare riferimento alla employeestabella. Cioè, la chiave esterna con cui hai avuto problemi dovrebbe fare riferimento a quella tabella!


17
Come fai a escludere a vicenda ce e sn? Poiché un dipendente non può essere contemporaneamente ce e sn, sarebbe buona norma riportarlo nel database. Ho questo problema in questo momento.
Rolf

Penso che più chiavi di colonna potrebbero aiutare con il problema nel mio commento precedente ... cercandolo in questo momento.
Rolf

12
È possibile forzare il dipendente a essere solo in una tabella (e in quella corretta) memorizzando un tipo nella tabella di base e nelle tabelle derivate. Rendi l'ID della chiave primaria, una chiave univoca su (id, type), la chiave esterna delle tabelle figlie (id, type) e metti un vincolo CHECK su ogni tabella figlia per avere solo il tipo corretto. Oppure, se il tuo database esegue i vincoli di controllo globale (e senza un'enorme penalità di velocità), puoi ovviamente eseguire un controllo NON ESISTENTE.
derobert

Controlla questa risposta per una spiegazione completa e dettagli sull'implementazione.
PerformanceDBA

come sapere che un dipendente con un ID specifico è "se" o "sn"?
mhrsalehi

22

Probabilmente puoi aggiungere due vincoli di chiave esterna (onestamente: non l'ho mai provato), ma insisterà che la riga padre esista in entrambe le tabelle.

Invece probabilmente vorrai creare un supertipo per i tuoi due sottotipi di dipendente e quindi puntare lì la chiave esterna. (Supponendo che tu abbia una buona ragione per dividere i due tipi di dipendenti, ovviamente).

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

typenella tabella dei dipendenti sarebbe ceo sn.


Ho provato ad aggiungere più chiavi esterne, hanno funzionato ma, durante l'aggiunta di un record, java derby mi dice che entrambi i vincoli della chiave esterna sono stati violati!

L'ho appena provato su PostgreSQL e funziona lì. Avevi il record principale in entrambe le tabelle?
derobert

record genitore intendi dire, l'empido?

Sicuramente il problema è stato spostato dalla tabella delle "detrazioni" alla tabella dei "dipendenti". Come faresti a fare riferimento a entità potenzialmente diverse in base a un tipo?
gawpertron

1
@gawpertron: Beh, l'empid è unico in tutti i tipi. Puoi utilizzare il campo "tipo" per vedere a quale sotto-tabella devi fare riferimento. O solo LEFT JOINtutti, se ce ne sono abbastanza. Quando non si utilizza la tabella di base "dipendente", non è stato possibile dichiarare la chiave primaria (perché fa riferimento a tabellaA o tabellaB o ...); ora può essere. La saggezza di scissione employees_cee employees_snstata assunta, e che ipotesi si nota.
derobert

19

In realtà lo faccio da solo. Ho una tabella chiamata "Commenti" che contiene commenti per i record in altre 3 tabelle. Nessuna delle due soluzioni gestisce effettivamente tutto ciò che probabilmente desideri. Nel tuo caso, faresti questo:

Soluzione 1:

  1. Aggiungi un campo tinyint a dipendenti_ce e dipendenti_sn che ha un valore predefinito diverso in ogni tabella (questo campo rappresenta un 'identificatore di tabella', quindi li chiameremo tid_ce e tid_sn)

  2. Crea un indice univoco su ogni tabella utilizzando il PK della tabella e il campo ID tabella.

  3. Aggiungi un campo tinyint alla tua tabella "Deduzioni" per memorizzare la seconda metà della chiave esterna (l'ID tabella)

  4. Crea 2 chiavi esterne nella tua tabella "Detrazioni" (Non puoi applicare l'integrità referenziale, perché una chiave sarà valida o l'altra ... ma mai entrambe:

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

Soluzione 2: questa soluzione consente di mantenere l'integrità referenziale: 1. Creare un secondo campo chiave esterna nella tabella "Deduzioni", consentire valori Null in entrambe le chiavi esterne e creare chiavi esterne normali:

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

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

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

L'integrità viene verificata solo se la colonna non è nulla, in modo da poter mantenere l'integrità referenziale.


6

So che questo è un argomento a lungo stagnante, ma nel caso qualcuno cerchi qui è come gestisco le chiavi esterne multi tabella. Con questa tecnica non hai nessuna operazione a cascata applicata da DBA, quindi assicurati di occuparti di DELETEqueste cose nel tuo codice.

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'esempio di SO Op sarebbe simile a questo

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

types
---------------------
1 employees_ce
2 employees_sn

1

Tecnicamente possibile. Probabilmente faresti riferimento a dipendenti_ce nelle detrazioni e dipendenti_sn. Ma perché non unisci dipendenti_sn e dipendenti_ce? Non vedo motivo per cui hai due tavoli. Nessuno a molti relazione. E (non in questo esempio) molte colonne.

Se esegui due riferimenti per una colonna, un dipendente deve avere una voce in entrambe le tabelle.


1

Sì, è possibile. Dovrai definire 2 FK per il 3 ° tavolo. Ogni FK che punta ai campi obbligatori di una tabella (cioè 1 FK per tabella straniera).


0

Supponendo che tu debba avere due tabelle per i due tipi di dipendenti per qualche motivo, estenderò la risposta di vmarquez:

Schema:

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

Dati in detrazione:

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

Ciò ti consentirebbe di fare in modo che le detrazioni puntino a qualsiasi altra tabella nel tuo schema. Questo tipo di relazione non è supportato da vincoli a livello di database, IIRC quindi dovrai assicurarti che la tua app gestisca il vincolo correttamente (il che lo rende più macchinoso se hai più app / servizi diversi che colpiscono lo stesso database).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.