Существует несколько вариантов моделирования этого.
Во-первых, делает main_man
также должно быть homie
? Если это так, я бы добавил флаг в таблицу homies
. Типы данных MySQL немного несовершенны, но я бы использовал логическое значение, которое мы всегда сопоставляем с типом данных TINYINT(1) DEFAULT NULL COMMENT 'boolean'
.
Следующий шаг - ограничить значения этого значения либо 1
, либо NULL
, не допускайте никаких других значений. К сожалению, MySQL не применяет ограничения CHECK, поэтому, если мы хотим, чтобы база данных обеспечивала соблюдение этого правила, нам нужно было бы реализовать триггеры BEFORE INSERT/BEFORE UPDATE
для его принудительного применения.
Наконец, мы бы добавить UNIQUE
Constraint
... ON homies (bro_id, main_man)
При том, что MySQL будет только разрешить один ряд с main_man значением 1
для каждого bro_id
.
Это небольшое отклонение от нормативного шаблона NULL, что означает «неизвестно», которое, как я вижу, поддерживается документацией Microsoft. В нашей реализации мы используем значение NULL для обозначения «no, not main_man». Основным преимуществом значений NULL
является то, что SQL (в общем) и MySQL, в частности, не считают значение NULL «дублирующимся» для другого значения NULL. Ограничение UNIQUE позволяет использовать несколько строк со значением NULL. (Я думаю, что есть какая-то установка sql_mode, которая изменяет это поведение, но мы никогда не туда.)
Чтобы получить только homies
, которые являются main_man
...
WHERE main_man = 1
или, более кратко, так как мы не используем ноль для представления TRUE, и если мы уверены, что никакие другие ненулевые значения не могут присутствовать ...
WHERE main_man
другая логика довольно проста, проверьте main_man IS NULL
или MAIN_MAN <=> NULL
, ORDER BY main_man, ...
, и верните столбец main_man
в SELECT
, если вы хотите отсортировать его на клиенте.
Вместо этого вы можете использовать тип данных MySQL ENUM
, если мы допустим значения NULL, и мы проверяем, что MySQL разрешит и обеспечит принудительное ограничение UNIQUE в столбце ENUM. (Я никогда не пробовал это раньше).
Это всего лишь один подход из нескольких, но это тот, который я успешно использовал в прошлом.
-
ДЕМОНСТРАЦИЯ
CREATE TABLE bro
(id INT UNSIGNED NOT NULL PRIMARY KEY
) ENGINE=INNODB;
CREATE TABLE homie
(id INT UNSIGNED NOT NULL PRIMARY KEY
, bro_id INT UNSIGNED NOT NULL COMMENT 'FK ref bros.id'
, main_man TINYINT(1) DEFAULT NULL COMMENT 'boolean, 1=is the main man'
, homie_name VARCHAR(10)
) ENGINE=INNODB;
ALTER TABLE homie
ADD UNIQUE INDEX homie_UX1 (bro_id, main_man);
ALTER TABLE homie
ADD CONSTRAINT FK_homie_bro FOREIGN KEY (bro_id) REFERENCES bro (id);
TODO: добавить ДО INSERT/ДО триггер UPDATE, чтобы ограничить значение для main_man
столбца.
Проверьте это, добавив несколько строк и убедитесь, что у нас не может быть более одного main_man
для данного bro_id
.
INSERT INTO bro (id) VALUES
(2),(3);
INSERT INTO homie (id, bro_id, main_man, homie_name) VALUES
(11, 2, NULL, 'mr.slate')
, (12, 2, 1, 'barney')
;
-- attempt to insert another main_man
INSERT INTO homie (id, bro_id, main_man, homie_name) VALUES
(13, 2, 1, 'wilma')
;
-- Error Code: 1062
-- Duplicate entry '2-1' for key 'homie_UX1'
UPDATE homie SET main_man = 1 WHERE id = 11 ;
-- Error Code: 1062
-- Duplicate entry '2-1' for key 'homie_UX1'
ПРИМЕЧАНИЕ: Я забыл упомянуть, как небольшой бонус, то homie_UX1
индекс (созданный для обеспечения соблюдения UNIQUE
ограничения) также служит для поддержки внешнего ключа, поскольку bro_id является ведущим колонки. Вот почему мы добавили индекс до того, как добавили ограничение внешнего ключа.