MySQL – 一对一的关系?
现在我知道关于一对一关系有一些答案,但他们都没有回答我的问题,所以请在表决之前阅读这个:)
我试图在MySQL数据库中实现一对一的关系。 例如让我说我有用户表和帐户表。 而且我想确定有用户只能有一个帐户。 而且每个用户只能有一个帐户。
我发现了两个解决scheme,但不知道该用什么,还有其他的select。
第一个scheme
DROP DATABASE IF EXISTS test; CREATE DATABASE test CHARSET = utf8 COLLATE = utf8_general_ci; USE test; CREATE TABLE users( id INT NOT NULL AUTO_INCREMENT, user_name VARCHAR(45) NOT NULL, PRIMARY KEY(id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; CREATE TABLE accounts( id INT NOT NULL AUTO_INCREMENT, account_name VARCHAR(45) NOT NULL, user_id INT UNIQUE, PRIMARY KEY(id), FOREIGN KEY(user_id) REFERENCES users(id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
在这个例子中,我定义了用户中指向主键的帐户中的外键。 然后我做外键独特,所以不能有两个相同的帐户用户。 要连接表,我会使用这个查询:
SELECT * FROM users JOIN accounts ON users.id = accounts.user_id;
解决scheme二
DROP DATABASE IF EXISTS test; CREATE DATABASE test CHARSET = utf8 COLLATE = utf8_general_ci; USE test; CREATE TABLE users( id INT NOT NULL AUTO_INCREMENT, user_name VARCHAR(45) NOT NULL, PRIMARY KEY(id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; CREATE TABLE accounts( id INT NOT NULL AUTO_INCREMENT, account_name VARCHAR(45) NOT NULL, PRIMARY KEY(id), FOREIGN KEY(id) REFERENCES users(id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
在这个例子中,我创build了从主键指向另一个表中的主键的外键。 由于主键默认情况下是唯一的,这使得这种关系一对一。 要连接表,我可以使用这个:
SELECT * FROM users JOIN accounts ON users.id = accounts.id;
现在的问题是:
- 在MySQL中创build一对一关系的最佳方式是什么?
- 除了这两个之外还有其他解决scheme吗?
我正在使用MySQL Workbench,当我在EER图中deviseOne To One关系,并让MySQL Workbench产生SQL代码时,我得到了一对多的关系:S这让我感到困惑:S
如果我将这些解决scheme中的任何一个导入到MySQL Workbench EER图表中,则它将关系识别为一对多:这也是令人困惑的。
那么,在DDL中定义One to One关系的最好方法是什么? 有什么select来实现这一目标?
谢谢!!
由于主键默认情况下是唯一的,这使得这种关系一对一。
不,这使关系“一个零或一个”。 这是你真正需要的吗?
如果是 ,那么你的“第二个解决scheme”更好:
- 它更简单,
- 占用更less的存储空间1 (因此使caching“更大”)
- 他less维护2的索引,这有利于数据操纵,
- 和(因为您使用的是InnoDB)自然地将数据集群在一起,因此靠近在一起的用户也将他们的帐户紧密地存储在一起,这可能有利于caching局部性和某些types的范围扫描。
顺便说一句,你需要使accounts.id
一个普通的整数(不自动增量)这个工作。
如果不是 ,请看下面…
在MySQL中创build一对一关系的最佳方式是什么?
那么,“最好”是一个重载词,但是“标准”解决scheme将与其他数据库相同:将两个实体(用户和帐户)放在同一个物理表中。
除了这两个之外还有其他解决scheme吗?
理论上讲,你可以在两个PK之间创build循环FK,但是这需要延迟约束来解决鸡与蛋问题,这在MySQL下是不受支持的。
如果我将这些解决scheme中的任何一个导入到MySQL Workbench EER图表中,则它将关系识别为一对多:这也是令人困惑的。
我对这个特定的build模工具没有太多的实践经验,但是我猜这是因为它是“一对多”,其中“许多”方面是独一无二的。 请记住,“许多”并不意味着“一个或多个”,这意味着“0或许多”,所以“封顶”版本的意思是“0或1”。
1不仅在附加字段的存储费用中,而且在二级索引中。 而且,由于您使用的是总是将表集群化的 InnoDB,所以要注意,二级索引在集群表中比在堆表中更昂贵。
2 InnoDB 需要外键索引 。
您的第一个方法在帐户表中创build两个候选键: id
和user_id
。
因此,我build议第二种方法,即使用外键作为主键。 这个:
- 使用一个较less的列
- 允许您唯一标识每一行
- 允许您将帐户与用户进行匹配
那么下面的方法呢?
-
创build表用户
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
在
user_id
和account_id
上创build具有唯一索引的表帐户,其中包含与用户/帐户的外键关系以及user_id and account_id
上的主键CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
创build表user2account
CREATE TABLE `user2account` ( `user_id` int(11) NOT NULL, `account_id` int(11) NOT NULL, PRIMARY KEY (`user_id`,`account_id`), UNIQUE KEY `FK_account_idx` (`account_id`), UNIQUE KEY `FK_user_idx` (`user_id`), CONSTRAINT `FK_account` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`), CONSTRAINT `FK_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
虽然这个解决scheme在数据库中占有最大的空间,但是有一些优势。
- 把FK_Key放在用户表或账户表中,我希望是一对多的(用户有很多帐户…)
- 虽然此
user2account
方法主要用于定义多对多关系,但在user_id
和account_id
上添加UNIQUE约束将会阻止创build除一对一关系外的其他内容。
我在这个解决scheme中看到的主要优点是可以将工作分成不同的代码层或公司的部门
- 部门A负责创build用户,即使没有账户表的写入权限,这也是可能的
- 部门B负责创build帐户,即使没有对用户表的写入权限也是如此
- 部门C负责创build映射,即使没有对用户或帐户表的写入许可,这也是可能的
- 一旦C部分创build了一个映射,用户和帐户都不能被A部分或B部分删除,而不要求C部分先删除映射。