Разрешить/требовать только одной записи с общим FK, чтобы иметь «основной» флаг
-
27-10-2019 - |
Вопрос
Во -первых, я прошу прощения, если это обманывает - я подозреваю, что это может быть, но я не могу его найти.
Скажем, у меня есть таблица компаний:
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
S. is_primary
Флаг будет очищен по той же операции?
Я не слишком беспокоюсь о том, company_id
существует в companies
Таблица, мой PHP -код уже выполнил бы эту проверку до того, как я добрался до этой стадии (хотя, если есть способ сделать это в той же операции, я полагаю, было бы хорошо).
Когда я изначально подумал об этом, я подумал: «Это будет легко, я просто добавлю уникальный индекс через company_id
а также is_primary
столбцы "Но, очевидно, это не сработает, так как это ограничило бы меня одним первичным и одним неприемным контактом - любая попытка добавить третий контакт потерпит неудачу. Но я не могу почувствовать, что будет способ настроить уникальный Индекс, который дает мне минимальную функциональность, которая мне требуется, - отклонить попытку добавить второй первичный контакт или отклонить попытку оставить компанию без основного контакта.
Я знаю, что могу просто добавить primary_contact
поле к companies
стол с FK в contacts
Стол, но это кажется грязным. Мне не нравится идея о том, что обе таблицы имеют FK для другой - мне кажется, что один стол должен полагаться на другую, а не обе таблицы полагаются друг на друга. Думаю, я просто думаю, что со временем есть больше шансов, что что -то пойдет не так.
Подводить итоги:
- Как я могу ограничить таблицу контактов так, чтобы одна и только одна запись с данной
company_id
имеетis_primary
набор флага? - У кого -нибудь есть какие -либо мысли о том, является ли два таблица, имеющие FK друг с другом хорошей/плохой идеей?
Решение
Круговые рефенции между таблицами действительно грязные. Смотрите это (десятилетие) статья: 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)
Другие советы
Вы могли бы просто сделать запрос перед этим настройкой
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, хотя вместо этого типа обходного пути, т.е.
РЕДАКТИРОВАТЬ Метод без полагаться на контакт.
Альтернативный метод, прежде всего, удалить is_primary из контактов. Во -вторых, добавьте в компании поле «promiser_contact_id» в компании. В -третьих, при изменении первичного контакта просто измените то, что primary_contact_id, таким образом, предотвращая возможность того, что в любое время существует более 1 первичного контакта, и без необходимости триггеров и т. Д. На заднем плане.
Этот вариант будет работать нормально в любом двигателе, поскольку он просто обновляет поле int, любая зависимость от FK и т. Д. Можно добавить/удалить по мере необходимости, но в простом его просто
Этот вариант жизнеспособен, если вам нужна одна из них, и именно одна ссылка от компаний на контакты, отмечающие первичные