Внешний ключ относится к первичным ключам в нескольких таблицах?


92

У меня есть две таблицы, а именно employee_ce и employee_sn под сотрудниками базы данных.

У них обоих есть соответствующие уникальные столбцы первичного ключа.

У меня есть еще одна таблица, называемая вычетами, столбец внешнего ключа которой я хочу сослаться на первичные ключи employee_ce, а также employee_sn. Это возможно?

например

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

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

так возможно ли это?

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

Ответы:


99

Предполагая, что я правильно понял ваш сценарий, я бы назвал это правильным способом:

Начните с более высокого уровня описания вашей базы данных! У вас есть сотрудники, и сотрудники могут быть сотрудниками «ce» и «sn» (какими бы они ни были). В объектно-ориентированных терминах существует класс «сотрудник» с двумя подклассами, которые называются «ce employee» и «sn employee».

Затем перевести это описание более высокого уровня для трех таблиц: employees, employees_ceа employees_sn:

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

Поскольку все сотрудники являются сотрудниками (да!), У каждого сотрудника будет строка в employeesтаблице. Сотрудники "ce" также имеют строку в employees_ceтаблице, а сотрудники "sn" также имеют строку в employees_snтаблице. employees_ce.idявляется внешним ключом к employees.id, как employees_sn.idесть.

Чтобы обратиться к сотруднику любого типа (CE или SN), обратитесь к employeesтаблице. То есть внешний ключ, с которым у вас возникли проблемы, должен ссылаться на эту таблицу!


17
Как сделать ce и sn взаимоисключающими? Поскольку сотрудник не может быть одновременно ce и sn, было бы неплохо отразить это в базе данных. У меня сейчас эта проблема.
Rolf

Я думаю, что несколько ключей столбцов могут помочь с проблемой в моем предыдущем комментарии ... ищу это прямо сейчас.
Rolf

12
Вы можете заставить сотрудника находиться только в одной таблице (и правильной), сохранив тип в базовой таблице, а также в производных таблицах. Сделайте идентификатор первичного ключа, уникальный ключ (идентификатор, тип), внешний ключ дочерних таблиц (идентификатор, тип) и установите ограничение CHECK для каждой дочерней таблицы, чтобы иметь только правильный тип. Или, если ваша база данных выполняет глобальные ограничения проверки (и без огромного снижения скорости), вы, конечно, можете просто выполнить проверку NOT EXISTS.
derobert

Проверьте этот ответ для получения полного объяснения и деталей реализации.
PerformanceDBA

как узнать сотрудника с определенным идентификатором «se» или «sn»?
mhrsalehi

22

Вы, вероятно, можете добавить два ограничения внешнего ключа (честно: я никогда не пробовал), но тогда он будет настаивать на существовании родительской строки в обеих таблицах.

Вместо этого вы, вероятно, захотите создать супертип для двух подтипов ваших сотрудников, а затем вместо этого указать внешний ключ. (Конечно, при условии, что у вас есть веская причина разделить сотрудников на два типа).

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

typeв таблице сотрудников будет ceили sn.


Я пробовал добавить несколько внешних ключей, они работали, но при добавлении записи java derby сообщает мне, что оба ограничения внешнего ключа были нарушены!

Я только что попробовал на PostgreSQL, и он там работает. Была ли у вас родительская запись в обеих таблицах?
derobert

родительская запись, вы хотите сказать, empid?

Наверняка проблема перенесена из таблицы «отчисления» в таблицу «служащий». Как бы вы сослались на потенциально разные сущности в зависимости от типа?
gawpertron

1
@gawpertron: Ну, empid уникален для всех типов. Вы можете использовать поле «Тип», чтобы увидеть, на какую подтаблицу нужно ссылаться. Или просто LEFT JOINвсе, если их мало. Если не используется базовая таблица «employee», первичный ключ не может быть объявлен (потому что он ссылается на tableA, tableB или…); теперь это возможно. Мудрость расщепления employees_ceи employees_snбыла принята, и это предположение отмечено.
derobert

20

Собственно и сам делаю. У меня есть таблица под названием «Комментарии», которая содержит комментарии к записям в трех других таблицах. Ни одно из решений на самом деле не справляется со всем, что вам, вероятно, нужно. В вашем случае вы бы сделали это:

Решение 1:

  1. Добавьте поле tinyint в employee_ce и employee_sn со значением по умолчанию, которое отличается в каждой таблице (это поле представляет собой «идентификатор таблицы», поэтому мы назовем их tid_ce и tid_sn)

  2. Создайте уникальный индекс для каждой таблицы, используя PK таблицы и поле идентификатора таблицы.

  3. Добавьте поле tinyint в таблицу «Вычеты» для хранения второй половины внешнего ключа (идентификатор таблицы).

  4. Создайте 2 внешних ключа в своей таблице «Вычеты» (вы не можете обеспечить ссылочную целостность, потому что либо один ключ будет действительным, либо другой ... но никогда оба:

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

Решение 2. Это решение позволяет поддерживать ссылочную целостность: 1. Создайте второе поле внешнего ключа в таблице «Вычеты», разрешите нулевые значения в обоих внешних ключах и создайте обычные внешние ключи:

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

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

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

Целостность проверяется только в том случае, если столбец не равен нулю, поэтому вы можете поддерживать ссылочную целостность.


6

Я знаю, что это давно застойная тема, но, если кто-то ищет, вот как я справляюсь с внешними ключами с несколькими таблицами. При использовании этого метода у вас нет каких-либо принудительных каскадных операций DBA, поэтому убедитесь, что вы имеете дело с DELETEи подобными в своем коде.

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)

Пример SO Op будет выглядеть так

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

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

1

Технически возможно. Вы, вероятно, будете ссылаться на employee_ce в вычетах и ​​employee_sn. Но почему бы вам не объединить employee_sn и employee_ce? Я не вижу причин, почему у вас два стола. Отношения «никто ко многим». И (не в этом примере) много столбцов.

Если вы делаете две ссылки для одного столбца, у сотрудника должна быть запись в обеих таблицах.


1

Да, это возможно. Вам нужно будет определить 2 FK для 3-й таблицы. Каждый FK указывает на обязательное поле (поля) одной таблицы (т. Е. 1 FK на внешнюю таблицу).


0

Предполагая, что по какой-то причине у вас должны быть две таблицы для двух типов сотрудников, я расширю ответ vmarquez:

Схема:

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

Данные в отчислениях:

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

Это позволит вам использовать вычеты для любой другой таблицы в вашей схеме. Этот вид отношений не поддерживается ограничениями уровня базы данных, IIRC, поэтому вам нужно убедиться, что ваше приложение правильно управляет ограничением (что делает его более громоздким, если у вас есть несколько разных приложений / служб, обращающихся к одной и той же базе данных).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.