Зовнішній ключ, що стосується первинних ключів у декількох таблицях?


91

У мене є дві таблиці, а саме staff_ce та staff_sn під базою даних співробітники.

Обидва вони мають свої унікальні стовпці первинного ключа.

У мене є інша таблиця, яка називається відрахування, у стовпці зовнішнього ключа якої я хочу посилатися на первинні ключі staff_ce, а також staff_sn. Чи можливо це?

наприклад

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

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

так чи можливо це?

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

Відповіді:


98

Якщо припустити, що я правильно зрозумів ваш сценарій, це я назвав би правильним способом зробити це:

Почніть з опису вашої бази даних на вищому рівні! У вас є співробітники, і співробітники можуть бути працівниками "ce" та "sn" (якими б вони не були). В об'єктно-орієнтованих термінах існує клас "працівник", з двома підкласами, що називаються "це співробітник" і "sn працівник".

Потім перевести це опис більш високого рівня для трьох таблиць: 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
Як зробити це та sn взаємовиключними? Оскільки працівник не може бути одночасно ce та sn, було б гарною практикою відобразити це в базі даних. Зараз у мене ця проблема.
Рольф

Я думаю, що кілька ключів стовпців можуть допомогти вирішити проблему в моєму попередньому коментарі ... шукаючи це прямо зараз.
Рольф

12
Ви можете змусити працівника знаходитись лише в одній таблиці (і правильній), зберігаючи тип у базовій таблиці, а також у похідних таблицях. Зробіть ідентифікатор первинного ключа, унікальний ключ (ідентифікатор, тип), зовнішній ключ дочірніх таблиць увімкнено (ідентифікатор, тип) і встановіть обмеження CHECK на кожну дочірню таблицю, щоб мати лише правильний тип. Або, якщо ваша база даних виконує глобальні обмеження перевірки (і без величезного покарання за швидкість), ви, звичайно, можете просто перевірити НЕ ІСНУЄ.
derobert

Перевірте цю відповідь, щоб отримати повне пояснення та деталі реалізації.
PerformanceDBA

як дізнатися працівника з конкретним ідентифікатором "se" або "sn"?
mhrsalehi

22

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

Натомість ви, ймовірно, хочете створити супертип для своїх двох підтипів співробітників, а потім натомість навести туди зовнішній ключ. (Припускаючи, що у вас є поважна причина розділити два типи працівників, звичайно).

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

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


Я спробував додати кілька зовнішніх ключів, вони працювали, але, додаючи запис, Java-дербі повідомляє, що обидва обмеження зовнішнього ключа були порушені!

Я щойно спробував його на PostgreSQL, і він працює там. У вас був батьківський запис в обох таблицях?
derobert

батьківський запис ви маєте на увазі сказати, емід?

Звичайно, проблема була перенесена з таблиці "відрахування" на таблицю "співробітник". Як би ви посилалися на потенційно різні сутності на основі типу?
gawpertron

1
@gawpertron: Ну, емпід унікальний для всіх типів. Ви можете використовувати поле "type", щоб побачити, на яку підтаблицю потрібно посилатися. Або просто LEFT JOINвсі, якщо їх недостатньо. Коли не використовується базова таблиця "співробітник", первинний ключ не може бути оголошений (оскільки він посилається на таблицю A, таблицю B або…); тепер це може бути. Мудрість розколу employees_ceі employees_snбула припущена, і це припущення зазначено.
derobert

19

Власне я роблю це сам. У мене є таблиця "Коментарі", яка містить коментарі до записів у 3 інших таблицях. Жодне рішення насправді не обробляє все, що ви, мабуть, хочете. У вашому випадку ви зробите це:

Рішення 1:

  1. Додайте поле tinyint до zaposlenih_ce та staff_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. Створіть друге поле зовнішнього ключа в таблиці „Відрахування”, дозвольте значення Null в обох зовнішніх ключах і створіть звичайні зовнішні ключі:

    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)

ТАК Приклад Опа мав би виглядати так

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

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

1

Технічно можливо. Ви, мабуть, посилаєтесь на staff_ce у відрахуваннях і staff_sn. Але чому б вам не об’єднати staff_sn та staff_ce? Я не бачу жодної причини, чому у вас є дві таблиці. Ніхто до багатьох стосунків. І (не в цьому прикладі) багато стовпців.

Якщо ви робите два посилання на один стовпець, працівник повинен мати запис в обох таблицях.


1

Так, це можливо. Вам потрібно буде визначити 2 ФК для 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.