有两种建模方法可供选择。
首先,main_man
也必须是homie
?如果是这样,我会在homies
表上添加一个标志。 MySQL数据类型有点不完美,但我会使用一个布尔值,我们总是映射到一个TINYINT(1) DEFAULT NULL COMMENT 'boolean'
数据类型。
下一步是限制其值为1
或NULL
,不允许任何其他值。不幸的是,MySQL不强制执行CHECK约束,所以如果我们希望数据库执行这个规则,我们需要实施BEFORE INSERT/BEFORE UPDATE
触发器来执行它。
最后,我们会增加一个UNIQUE
约束
... ON homies (bro_id, main_man)
就这样,MySQL将只允许一个单行的1
每个bro_id
一个main_man值。
这与微软文档支持的NULL意义“未知”的规范模式略有偏差。在我们的实现中,我们使用NULL值表示“不,不是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, ...
,如果要在客户端上对其进行排序,则返回SELECT
中的main_man
列。
您可能会考虑使用MySQL ENUM
数据类型,只要我们允许NULL值,并验证MySQL将允许并在ENUM列上强制实施UNIQUE约束。 (我从来没有尝试过)。
这只是几种方法之一,但它是我过去成功使用的方法之一。
-
示范
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:添加BEFORE INSERT/BEFORE UPDATE触发器用于main_man
列限制值。
通过添加一些行来测试此操作,并检查给定的bro_id
我们不能有多于一个main_man
。
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是领先的列。这就是我们在添加外键约束之前添加索引的原因。