繁体   English   中英

MySQL - 一对一关系?

[英]MySQL - One To One Relationship?

我正在尝试在 MySQL 数据库中实现“一对一”关系。 例如,假设我有一个用户表和一个帐户表。 我想确保一个用户只能拥有一个帐户。 并且每个用户只能有一个帐户。

我为此找到了两种解决方案,但不知道该使用什么,还有其他选择吗?

第一个解决方案:

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;

在这个例子中,我在 accounts 中定义外键指向 users 中的主键。 然后我将外键设置为唯一,因此帐户中不能有两个相同的用户。 要连接表,我将使用此查询:

SELECT * FROM users JOIN accounts ON users.id = accounts.user_id;

第二种解决方案:

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;

在此示例中,我创建了一个从主键指向另一个表中的主键的外键。 由于默认情况下主键是唯一的,因此这种关系是一对一的。 要加入表格,我可以使用这个:

SELECT * FROM users JOIN accounts ON users.id = accounts.id;

现在的问题:

  • 在 MySQL 中创建一对一关系的最佳方法是什么?
  • 除了这两个还有其他解决方案吗?

我正在使用 MySQL Workbench,当我在 EER 图中设计一对一关系并让 MySQL Workbench 生成 SQL 代码时,我得到一对多关系:S 这让我感到困惑:S

如果我将这些解决方案中的任何一个导入 MySQL Workbench EER 图中,它会将关系识别为一对多:S 这也令人困惑。

那么,在 MySQL DDL 中定义一对一关系的最佳方式是什么。 有哪些选择可以实现这一目标?

由于默认情况下主键是唯一的,因此这种关系是一对一的。

不,这使得关系“一对零或一”。 那是你真正需要的吗?

如果,那么您的“第二个解决方案”更好:

  • 它更简单,
  • 占用更少的存储空间1 (因此使缓存“更大”)
  • 他需要维护的索引更少2 ,这有利于数据操作,
  • 并且(因为您使用的是 InnoDB)自然地对数据进行集群,因此靠近的用户也会将他们的帐户存储在一起,这可能有利于缓存局部性和某些类型的范围扫描。

顺便说一句,您需要将accounts.id设置为普通整数(不是自动递增)才能正常工作。

如果没有,请看下面...

在 MySQL 中创建一对一关系的最佳方法是什么?

嗯,“最好”是一个重载的词,但“标准”解决方案与任何其他数据库中的解决方案相同:将两个实体(在您的情况下为用户和帐户)放在同一个物理表中。

除了这两个还有其他解决方案吗?

理论上,您可以在两个 PK 之间创建循环 FK,但这将需要延迟约束来解决先有鸡还是先有蛋的问题,不幸的是,MySQL 不支持这种问题。

如果我将这些解决方案中的任何一个导入 MySQL Workbench EER 图中,它会将关系识别为一对多:S 这也令人困惑。

我对那个特定的建模工具没有太多的实践经验,但我猜这是因为它是“一对多”的,其中“多”边通过使其唯一而被限制在 1。 请记住,“许多”并不意味着“1 或许多”,它意味着“0 或许多”,因此“上限”版本真正意味着“0 或 1”。


1不仅是附加字段的存储费用,还有二级索引的存储费用。 并且由于您使用的 InnoDB 始终对表进行聚簇,因此请注意,聚簇表中的二级索引比基于堆的表中的二级索引更昂贵。

2 InnoDB 需要外键索引

您的第一种方法在 accounts 表中创建两个候选键: iduser_id

因此,我建议采用第二种方法,即使用外键作为主键。 这个:

  • 使用少一列
  • 允许您唯一标识每一行
  • 允许您将帐户与用户匹配

下面的方法呢

  1. 创建表用户

    CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  2. user_idaccount_id上创建具有唯一索引的表帐户,与用户/帐户的外键关系以及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;
  3. 创建表 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;

虽然此解决方案在数据库中的占用空间最大,但也有一些优势。

  • 将 FK_Key 放在用户表或帐户表中是我希望成为一对多关系的东西(用户有很多帐户......)
  • 虽然这种user2account方法主要用于定义多对多关系,但在user_idaccount_id上添加 UNIQUE 约束将防止创建除一对一关系之外的其他关系。

我在这个解决方案中看到的主要优点是您可以将工作划分到公司的不同代码层或部门

  • 部门A负责创建用户,即使没有accounts表的写权限也是可以的
  • B部门负责创建账户,即使没有对用户表的写权限也是可能的
  • 部门 C 负责创建映射,即使没有对用户或帐户表的写权限也是可能的
  • 一旦部门 C 创建了映射,部门 A 或 B 就不能删除用户和帐户,除非先要求部门 C 删除映射。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM