MySQL - 一对一的关系?

Lim*_*eni 54 mysql sql database database-design one-to-one

现在我知道有一对一关系的答案,但他们都没有回答我的问题,所以请在下来之前阅读这个:)

我试图在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;
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM users JOIN accounts ON users.id = accounts.user_id;
Run Code Online (Sandbox Code Playgroud)

二解决方案:

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;
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM users JOIN accounts ON users.id = accounts.id;
Run Code Online (Sandbox Code Playgroud)

现在的问题是:

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

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

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

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

谢谢!!

Bra*_*vic 42

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

不,这使关系"一到零或一".那是你真正需要的吗?

如果,那么你的"第二个解决方案"会更好:

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

顺便说一下,你需要制作accounts.id一个普通的整数(不是自动增量)来实现这一点.

如果不是,请参阅下文......

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

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

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

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

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

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


1不仅在附加字段的存储费用中,而且在二级索引中也是如此.由于您使用的InnoDB 始终聚集表,因此请注意,在集群表中,二级索引比在基于堆的表中更昂贵.

2 InnoDB 需要外键索引.


Sal*_*n A 9

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

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

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

  • 我认为使用ID作为外键可能会遇到麻烦.您无法保证两个ID都相同,因为数据库自动生成它们(理论上它们*应该*始终相同,但您不能假设它).事实上,由于您的示例没有为account-> user关系定义NOT NULL,我认为可以拥有一个没有用户的帐户或其他方式?如果您曾经遇到过创建多个用户的情况,然后以不同的顺序创建了他们的帐户,那么您的关系就会中断. (3认同)
  • 我认为这是更清洁的解决方案.实际上,如果你总是同时创建和删除这两个表,使用ID可能永远不会真正导致问题,但是如果你遇到了ID不知何故不同步的边缘情况,你可能会发现自己处于一个世界痛苦 (2认同)