MySQL - 一对一的关系?

时间:2012-11-30 12:09:01

标签: mysql sql database database-design one-to-one

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

我找到了两个解决方案,但不知道该使用什么,还有其他选择。

第一个解决方案:

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;

在此示例中,我在指向用户主键的帐户中定义外键。 然后我将外键设为UNIQUE,因此帐户中不能有两个相同的用户。 要连接表,我会使用此查询:

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;

在此示例中,我创建了一个外键,该外键从主键指向另一个表中的主键。由于主键默认为UNIQUE,因此这种关系一对一。 要连接表格,我可以使用:

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

现在问题:

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

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

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

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

3 个答案:

答案 0 :(得分:40)

  

由于主键默认为UNIQUE,因此这种关系是一对一的。

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

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

  • 更简单,
  • 占用较少的存储 1 (因此使缓存“更大”)
  • 维护 2 的索引更少,这有利于数据操作,
  • 和(因为您正在使用InnoDB)自然clusters数据,因此靠近的用户也会将他们的帐户存储在一起,这可能有利于缓存局部性和某些范围的扫描。

顺便说一下,你需要让accounts.id成为一个普通的整数(不是自动增量)才能生效。

如果,请参阅以下内容......

  

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

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

  

除了这两种解决方案之外还有其他解决方案吗?

理论上,你可以在两个PK之间制作循环FK,但这需要 deferred 约束来解决鸡与蛋的问题,遗憾的是MySQL不支持这个问题。

  

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

我对这个特定的建模工具没有太多的实际经验,但我猜这是因为它是“一对多”,其中“很多”方面通过使其独特而被限制为1。请记住,“很多”并不代表“1或多”,它的意思是“0或许多”,所以“上限”版本的确意味着“0或1”。


1 不仅在附加字段的存储费用中,而且在二级索引中也是如此。因为您正在使用always clusters tables的InnoDB,请注意,在集群表中,二级索引比在基于堆的表中更昂贵。

外键上的

2 InnoDB requires indexes

答案 1 :(得分:8)

您的第一种方法是在帐户表格中创建两个候选键:iduser_id

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

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

答案 2 :(得分:-2)

以下方法如何

  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;
    
  4. 虽然此解决方案在数据库中具有最大的占用空间,但仍有一些优势。

    • 将FK_Key放在用户表或帐户表中是我期望的一对多关联(用户拥有多个帐户......)
    • 虽然此user2account方法主要用于定义多对多关系,但在user_idaccount_id上添加UNIQUE约束会阻止创建除一对一关系之外的其他关系

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

    • 部门A负责创建用户,即使没有对帐户表的书面许可,也可以这样做
    • 部门B负责创建帐户,即使没有对用户表的书面许可,也可以这样做
    • 部门C负责创建映射,即使没有对用户或帐户表的书面许可也可以这样做
    • 一旦部门C创建了映射,用户和帐户都不能被部门A或B删除,而不要求部门C首先删除映射。