Chave estrangeira se referindo a chaves primárias em várias tabelas?

91

Eu tenho duas tabelas, ou seja, funcionários_ce e funcionários_sn, no banco de dados funcionários.

Ambos têm suas respectivas colunas de chave primária exclusivas.

Eu tenho outra tabela chamada deduções, cuja coluna de chave estrangeira desejo fazer referência às chaves primárias de funcionários_ce, bem como funcionários_sn. Isso é possível?

por exemplo

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

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

então isso é possível?

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

Respostas:

98

Supondo que eu tenha entendido o cenário corretamente, isso é o que eu chamaria de direito maneira de fazer isso:

Comece com uma descrição de nível superior de seu banco de dados! Você tem funcionários, e eles podem ser funcionários "ce" e funcionários "sn" (sejam eles quais forem). Em termos orientados a objetos, existe uma classe "funcionário", com duas subclasses denominadas "funcionário ce" e "funcionário sn".

Então você traduzir esta descrição de nível superior para três tabelas: employees, employees_cee employees_sn:

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

Como todos os funcionários são funcionários (duh!), Cada funcionário terá uma linha na employeestabela. Os funcionários "ce" também têm uma linha na employees_cetabela e os funcionários "sn" também têm uma linha na employees_sntabela. employees_ce.idé uma chave estrangeira para employees.id, exatamente como employees_sn.idestá.

Para se referir a um funcionário de qualquer tipo (ce ou sn), consulte a employeestabela. Ou seja, a chave estrangeira com a qual você teve problemas deve referir-se a essa tabela!

Thomas Padron-McCarthy
fonte
17
Como você torna ce e sn mutuamente exclusivos? Como um funcionário não pode estar ao mesmo tempo ce e sn, seria uma boa prática refletir isso no banco de dados. Estou tendo esse problema agora.
Rolf
Acho que várias chaves de coluna podem ajudar com o problema em meu comentário anterior ... pesquisando isso agora.
Rolf
12
Você pode forçar o funcionário a estar em apenas uma tabela (e a correta), armazenando um tipo na tabela base, bem como nas tabelas derivadas. Faça o id da chave primária, uma chave única em (id, tipo), a chave estrangeira das tabelas filhas (id, tipo) e coloque uma restrição CHECK em cada tabela filha para ter apenas o tipo correto. Ou, se seu banco de dados faz restrições de verificação global (e sem grande penalidade de velocidade), você pode, é claro, apenas fazer uma verificação NOT EXISTS.
derobert
Verifique esta resposta para uma explicação completa e detalhes de implementação.
PerformanceDBA
como saber se um funcionário com um id específico é 'se' ou 'sn'?
mhrsalehi
22

Você provavelmente pode adicionar duas restrições de chave estrangeira (honestamente: nunca tentei), mas ele insistiria que a linha pai existe em ambas as tabelas.

Em vez disso, você provavelmente deseja criar um supertipo para seus dois subtipos de funcionários e, em seguida, apontar a chave estrangeira para lá. (Supondo que você tenha um bom motivo para dividir os dois tipos de funcionários, é claro).

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

typena tabela de funcionários seria ceou sn.

Derobert
fonte
Tentei adicionar várias chaves estrangeiras, elas funcionaram, mas, ao adicionar um registro, o java derby me disse que ambas as restrições de chave estrangeira foram violadas!
Acabei de experimentar no PostgreSQL e funciona lá. Você teve o registro pai em ambas as tabelas?
derobert em
registro dos pais, você quer dizer, o vazio?
Certamente, o problema foi transferido da tabela de "deduções" para a tabela de "funcionários". Como você referenciaria entidades potencialmente diferentes com base em um tipo?
gawpertron
1
@gawpertron: Bem, o empid é único em todos os tipos. Você pode usar o campo 'tipo' para ver qual subtabela você precisa fazer referência. Ou apenas LEFT JOINtodos eles, se houver poucos. Quando não estiver usando a tabela base 'funcionário', a chave primária não pode ser declarada (porque está fazendo referência à tabela A ou à tabela B ou ...); agora pode ser. A sabedoria de dividir employees_cee employees_snfoi assumida, e essa suposição é observada.
derobert
19

Na verdade, eu mesmo faço isso. Eu tenho uma tabela chamada 'Comentários' que contém comentários para registros em 3 outras tabelas. Nenhuma das soluções realmente lida com tudo o que você provavelmente deseja. No seu caso, você faria o seguinte:

Solução 1:

  1. Adicione um campo tinyint aos funcionários_ce e funcionários_sn que tem um valor padrão que é diferente em cada tabela (este campo representa um 'identificador de tabela', então vamos chamá-los de tid_ce & tid_sn)

  2. Crie um índice exclusivo em cada tabela usando o PK da tabela e o campo de id da tabela.

  3. Adicione um campo tinyint à sua tabela 'Deduções' para armazenar a segunda metade da chave estrangeira (o ID da tabela)

  4. Crie 2 chaves estrangeiras em sua tabela de 'Deduções' (você não pode impor integridade referencial, porque uma das chaves será válida ou a outra ... mas nunca ambas:

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

Solução 2: Esta solução permite que a integridade referencial seja mantida: 1. Crie um segundo campo de chave estrangeira na tabela 'Deduções', permita valores nulos em ambas as chaves estrangeiras e crie chaves estrangeiras normais:

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

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

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

A integridade só é verificada se a coluna não for nula, para que você possa manter a integridade referencial.

LittleC
fonte
6

Eu sei que este é um tópico estagnado há muito tempo, mas no caso de alguém pesquisar aqui, é como eu lido com chaves estrangeiras de várias tabelas. Com essa técnica, você não tem nenhuma operação em cascata imposta pelo DBA, portanto, certifique-se de lidar com DELETEisso em seu código.

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)

O exemplo da SO Op seria assim

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

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

Tecnicamente possível. Você provavelmente faria referência aos funcionários_ce em deduções e funcionários_sn. Mas por que você não funde funcionários_sn e funcionários_ce? Não vejo razão para você ter duas mesas. Relacionamento de ninguém para muitos. E (não neste exemplo) muitas colunas.

Se você fizer duas referências para uma coluna, um funcionário deve ter uma entrada em ambas as tabelas.

Sascha
fonte
1

Sim, é possível. Você precisará definir 2 FKs para a 3ª mesa. Cada FK apontando para o (s) campo (s) obrigatório (s) de uma tabela (ou seja, 1 FK por tabela estrangeira).

Vmarquez
fonte
0

Supondo que você deva ter duas tabelas para os dois tipos de funcionários por algum motivo, irei estender a resposta de vmarquez:

Esquema:

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

Dados em deduções:

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

Isso permitiria que as deduções apontassem para qualquer outra tabela em seu esquema. Este tipo de relação não é suportado por restrições de nível de banco de dados, IIRC, então você terá que garantir que seu aplicativo gerencie a restrição corretamente (o que torna mais complicado se você tiver vários aplicativos / serviços diferentes acessando o mesmo banco de dados).

r00fus
fonte