Разрешить / требовать только одну запись с общим FK, чтобы иметь «первичный» флаг - PullRequest
4 голосов
/ 17 февраля 2012

Во-первых, я прошу прощения, если это обман - я подозреваю, что это может быть, но я не могу найти его.

Скажите, у меня есть таблица компаний:

 id | company_name
----+--------------
  1 | Someone
  2 | Someone else

... и таблица контактов:

 id | company_id | contact_name | is_primary
----+------------+--------------+------------
  1 |     1      | Tom          |      1
  2 |     2      | Dick         |      1
  3 |     1      | Harry        |      0
  4 |     1      | Bob          |      0

Можно ли настроить таблицу contacts таким образом, чтобы требовалось , чтобы для одной и только одной записи был установлен флаг is_primary для каждой общей company_id?

Так что, если я попытался сделать:

UPDATE contacts
SET is_primary = 1
WHERE id = 4

... запрос не будет выполнен, поскольку Tom (id = 1) уже помечен как основной контакт для company_id = 1. Или, что еще лучше, можно ли построить триггер так, чтобы запрос выполнялся успешно, но флаг Tom is_primary будет сброшен той же самой операцией?

Меня не слишком беспокоит проверка того, существует ли company_id в таблице companies, мой PHP-код уже выполнил бы эту проверку, прежде чем я добрался до этой стадии (хотя, если есть способ сделать это в той же операция была бы неплохой, наверное).

Когда я сначала подумал об этом, я подумал: «это будет легко, я просто добавлю уникальный индекс в столбцы company_id и is_primary», но, очевидно, это не сработает, поскольку ограничит меня одним основной и один неосновной контакт - любая попытка добавить третий контакт потерпит неудачу. Но я не могу не чувствовать, что есть способ настроить уникальный индекс, который дает мне минимальные функциональные возможности, которые мне требуются - отклонить попытку добавления второго основного контакта или отклонить попытку покинуть компанию без основного контакта.

Мне известно, что я мог бы просто добавить поле primary_contact к таблице companies с FK к таблице contacts, но это выглядит грязно. Мне не нравится идея, что обе таблицы имеют FK для другой - мне кажется, что одна таблица должна опираться на другую, а не обе таблицы полагаться друг на друга. Я думаю, я просто думаю, что со временем есть больше шансов, что что-то пойдет не так.

Подводя итог:

  • Как я могу ограничить таблицу контактов, чтобы одна и только одна запись с данным company_id имела установленный флаг is_primary?
  • У кого-нибудь есть мысли о том, является ли две или две таблицы, имеющие FK друг другу, хорошей / плохой идеей?

Ответы [ 2 ]

3 голосов
/ 17 февраля 2012

Круговые рефены между таблицами действительно грязные. См. Эту (десятилетнюю) статью: SQL По замыслу: Циркулярная ссылка

Самый простой способ создать такое ограничение - добавить еще одну таблицу:

Company_PrimaryContact
----------------------
company_id
contact_id
PRIMARY KEY (company_id)
FOREIGN KEY (company_id, contact_id)
  REFERENCES Contact (company_id, id)

Для этого также потребуется ограничение UNIQUE в таблице Contact для (company_id, id)

2 голосов
/ 17 февраля 2012

Вы можете просто сделать запрос до этого параметра

UPDATE contacts SET is_primary = 0 WHERE company_id = .....

или даже

UPDATE contacts
SET is_primary = IF(id=[USERID],1,0)
WHERE company_id = (
    SELECT company_id FROM contacts WHERE id = [USERID]
);

Просто предлагая альтернативу - лично я бы, вероятно, обратился к подходу FK, хотя вместо этого типа обходного пути, т. Е. Есть поле в таблице компаний с полем primary_user_id.

РЕДАКТИРОВАТЬ метод без использования поля contact.is_primary

Альтернативный метод, в первую очередь удалить is_primary из контактов. Во-вторых, добавьте поле INT «primary_contact_id» в компании. В-третьих, при изменении первичного контакта просто измените этот primary_contact_id, исключив тем самым возможность наличия более одного первичного контакта в любое время и все без необходимости триггеров и т. Д. В фоновом режиме.

Эта опция будет работать нормально на любом движке, так как она просто обновляет поле INT, любая зависимость от FK и т. Д. Может быть добавлена ​​/ удалена по мере необходимости, но, проще всего, это просто изменение значения поля INT

Эта опция жизнеспособна, если вам нужна одна и точно одна ссылка от компаний на контакты, помечающие основной

...