¿Clave externa que se refiere a claves primarias en varias tablas?

91

Tengo dos tablas, a saber, empleados_ce y empleados_sn en la base de datos empleados.

Ambos tienen sus respectivas columnas de clave primaria únicas.

Tengo otra tabla llamada deducciones, cuya columna de clave externa quiero hacer referencia a las claves primarias de employee_ce así como de employee_sn. es posible?

por ejemplo

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

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

entonces, ¿es esto posible?

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

Respuestas:

98

Suponiendo que he entendido su escenario correctamente, esto es lo que llamaría la forma correcta de hacer esto:

¡Comience con una descripción de nivel superior de su base de datos! Tiene empleados, y los empleados pueden ser empleados "ce" y empleados "sn" (sean los que sean). En términos orientados a objetos, hay una clase "empleado", con dos subclases llamadas "empleado ce" y "empleado sn".

Entonces usted traduce esta descripción de alto nivel de tres tablas: employees, employees_cey employees_sn:

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

Dado que todos los empleados son empleados (¡obvio!), Cada empleado tendrá una fila en la employeestabla. Los empleados "ce" también tienen una fila en la employees_cetabla, y los empleados "sn" también tienen una fila en la employees_sntabla. employees_ce.ides una clave externa para employees.id, tal como employees_sn.idestá.

Para referirse a un empleado de cualquier tipo (ce o sn), consulte la employeestabla. Es decir, la clave externa con la que tuvo problemas debe hacer referencia a esa tabla.

Thomas Padron-McCarthy
fuente
17
¿Cómo se hace ce y sn mutuamente excluyentes? Dado que un empleado no puede ser al mismo tiempo ce y sn, sería una buena práctica reflejarlo en la base de datos. Tengo este problema ahora mismo.
Rolf
Creo que varias claves de columna podrían ayudar con el problema de mi comentario anterior ... buscando eso ahora mismo.
Rolf
12
Puede obligar al empleado a estar en una sola tabla (y en la correcta) almacenando un tipo en la tabla base, así como en las tablas derivadas. Haga que la identificación de la clave principal, una clave única en (id, tipo), la clave externa de las tablas secundarias esté activada (id, tipo) y coloque una restricción CHECK en cada tabla secundaria para tener solo el tipo correcto. O, si su base de datos tiene restricciones de verificación globales (y sin una gran penalización de velocidad), por supuesto, puede simplemente hacer una verificación NO EXISTE.
derobert
Consulte esta respuesta para obtener una explicación completa y los detalles de implementación.
PerformanceDBA
¿Cómo saber si un empleado con una identificación específica es 'se' o 'sn'?
mhrsalehi
22

Probablemente pueda agregar dos restricciones de clave externa (honestamente: nunca lo he probado), pero luego insistiría en que la fila principal existe en ambas tablas.

En su lugar, probablemente desee crear un supertipo para sus dos subtipos de empleados y luego apuntar la clave externa allí. (Suponiendo que tenga una buena razón para dividir los dos tipos de empleados, por supuesto).

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

typeen la tabla de empleados sería ceo sn.

derobert
fuente
Intenté agregar varias claves externas, funcionaron pero, al agregar un registro, ¡java derby me dice que se han violado ambas restricciones de clave externa!
Lo probé en PostgreSQL y funciona allí. ¿Tenía el registro principal en ambas tablas?
derobert
registro de los padres que quiere decir, el empid?
Seguramente el problema se ha trasladado de la tabla de "deducciones" a la tabla de "empleados". ¿Cómo haría referencia a Entidades potencialmente diferentes según un tipo?
gawpertron
1
@gawpertron: Bueno, el empid es único en todos los tipos. Puede utilizar el campo 'tipo' para ver a qué subtabla necesita hacer referencia. O solo LEFT JOINtodos, si hay pocos. Cuando no se utiliza la tabla base 'empleado', la clave principal no se puede declarar (porque hace referencia a tableA o tableB o…); ahora puede ser. Se asumió la sabiduría de dividir employees_cey employees_sn, y se toma nota de esa suposición.
derobert
19

De hecho, lo hago yo mismo. Tengo una tabla llamada 'Comentarios' que contiene comentarios para registros en otras 3 tablas. Ninguna solución maneja todo lo que probablemente quieras que haga. En tu caso, harías esto:

Solución 1:

  1. Agregue un campo tinyint a employee_ce y employee_sn que tenga un valor predeterminado que sea diferente en cada tabla (este campo representa un 'identificador de tabla', por lo que los llamaremos tid_ce & tid_sn)

  2. Cree un índice único en cada tabla utilizando el PK de la tabla y el campo de identificación de la tabla.

  3. Agregue un campo tinyint a su tabla 'Deducciones' para almacenar la segunda mitad de la clave externa (el ID de la tabla)

  4. Cree 2 claves externas en su tabla de 'Deducciones' (no puede hacer cumplir la integridad referencial, porque una clave será válida o la otra ... pero 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 **

Solución 2: Esta solución permite mantener la integridad referencial: 1. Cree un segundo campo de clave externa en la tabla 'Deducciones', permita valores nulos en ambas claves externas y cree claves externas normales:

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

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

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

La integridad solo se verifica si la columna no es nula, por lo que puede mantener la integridad referencial.

LittleC
fuente
6

Sé que este es un tema estancado durante mucho tiempo, pero en caso de que alguien busque aquí, así es como trato con las claves externas de múltiples tablas. Con esta técnica, no tiene ninguna operación en cascada impuesta por DBA, así que asegúrese de tratar con DELETEy tal en su 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)

El ejemplo de SO Op se vería así

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

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

Técnicamente posible. Probablemente haría referencia a employee_ce en las deducciones y employee_sn. Pero, ¿por qué no fusiona employee_sn y employee_ce? No veo ninguna razón por la que tengas dos mesas. Relación de nadie a muchos. Y (no en este ejemplo) muchas columnas.

Si hace dos referencias para una columna, un empleado debe tener una entrada en ambas tablas.

Sascha
fuente
1

Sí, es posible. Deberá definir 2 FK para la 3ª mesa. Cada FK apunta a los campos obligatorios de una tabla (es decir, 1 FK por tabla extranjera).

vmarquez
fuente
0

Suponiendo que debe tener dos tablas para los dos tipos de empleados por alguna razón, extenderé la respuesta de vmarquez:

Esquema:

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

Datos en deducciones:

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

Esto le permitiría hacer que las deducciones apuntaran a cualquier otra tabla en su esquema. Este tipo de relación no es compatible con restricciones a nivel de base de datos, IIRC, por lo que deberá asegurarse de que su aplicación administre la restricción correctamente (lo que lo hace más engorroso si tiene varias aplicaciones / servicios diferentes en la misma base de datos).

r00fus
fuente