假设我有两对一对多的关系表。我们将第一个称为Bros,第二个称为Homies。兄弟可以有多个兄弟,但其中只有一个可以成为他的“主要人物”。
(看,例子很难。不要对我大喊大叫。)
我怎么代表那个?我可以将一个'main_man'条目放入bros表中,但这会重复我在homies表中的条目。
我也可以在同性恋表中加入一个条目,但这并不会限制其他同性恋者成为主要人物。
有没有正确的方法呢?是否更容易以错误的方式执行并使用应用程序来处理它?</ p>
答案 0 :(得分:3)
有几种方法可以对此进行建模。
首先,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
的main_man值为bro_id
的单行。
这与NULL的规范模式略有不同,意思是“未知”,我认为这是由Microsoft文档所支持的。在我们的实现中,我们使用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, ...
,如果您愿意,请返回main_man
中的SELECT
列在客户端上解决它。
您可以考虑使用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
列的值。
通过添加一些行来对此进行测试,并检查给定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是前导列。这就是我们在添加外键约束之前添加索引的原因。
答案 1 :(得分:2)
以下是建立一对多关系的非常标准的方法,其中一个子行被认为是“特殊的”:
此模型具有以下重要属性:
总之,这两个属性确保:
但是,在并发环境中,您必须小心如何生成BRO_NO。一些可能性:
如果有其他表引用BRO,您可以考虑添加代理键(例如BRO_ID)。有关代理键的利弊,请参阅here。
顺便说一句,上面的模型有一个变化:失去反向FK,只考虑哪个兄弟将最小 BRO_NO视为特殊。如果您事先知道特殊兄弟,或者如果您不介意更新密钥(并且可能级联更改)以将兄弟移动到顶部,那么这很好。1 如果DBMS支持延迟约束,则父级中的FK可以为非NULL,并确保完全一个子级是特殊的(而不仅仅是零或一)。当存在圆形FK时插入新数据时,其中一个FK会破坏鸡和蛋的问题。不幸的是,MySQL不支持延迟约束。