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

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
Cœur
источник

Ответы:

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.

Derobert
источник
Я пробовал добавить несколько внешних ключей, они работали, но при добавлении записи 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         

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

LittleC
источник
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, поэтому вам нужно убедиться, что ваше приложение правильно управляет ограничением (что делает его более громоздким, если у вас есть несколько разных приложений / служб, обращающихся к одной и той же базе данных).

r00fus
источник