【问题标题】:MySQL primary key that I never select我从不选择的 MySQL 主键
【发布时间】:2019-06-24 05:31:53
【问题描述】:

我经常遇到这样的情况:

table `user_adress`
+----------+-------------+--------------+---------+
|adress_id | user_id     | adress_type  |adress   |
+----------+-------------+--------------+---------+
|        1 |           1 | home         |adressXXX|
|        2 |           2 | home         |adressXXX|
|        3 |           3 | home         |adressXXX|
|        4 |           1 | work         |adressXXX|
|        5 |           2 | work         |adressXXX|
|        6 |           1 | second_home  |adressXXX|
+----------+-------------+--------------+---------+

如果我想使用它,我会使用这样的查询:

SELECT `adress` FROM `user_adress` WHERE `user_id`=1;

看起来很正常,但问题是,我使用“无用”adress_id 列,它没有其他目的,只是为了在 MySQL 表中有一个主键而成为具有自动增量的主键。我从不使用或不需要这个号码。所以我发现我根本不应该在我的表中使用主键,完全删除adress_id,并在user_id 列设置INDEX(没有unique)。这似乎很好 - 还是我错了?

我有一些疑问,因为在我阅读的同时,我在任何地方看到的建议是,每个表都应该,甚至需要有主键。但为什么?如果我允许这种情况发生,也许我的数据库设计得很糟糕,但是看看我非常简单的示例表 - 我无法想象在每种情况下都会出现这种情况,尤其是在这种简单的情况下。我肯定误解了一些关于创建表和正确索引它们的简单基本规则——我的问题在哪里?

【问题讨论】:

  • 如何在不删除用户 1 的工作地址和 second_home 地址的情况下删除用户 1 的家庭地址?即使您对此有计划,如果不满足主键的假设,某些框架/环境也无法充分发挥作用。
  • 你完全正确,我扩展了示例表 - 我的错误!
  • user_id, address_type 可以被视为候选主键,但如果某人有三个家庭住址,则必须添加新的地址类型。不管怎样,你最终还是会得到一个主键。它只是复合的,任何引用地址的东西(可能是账单或送货地址)现在都必须在两个值而不是一个值上连接/查询。
  • 注意:使用非递增主键(或无主键)时,InnoDB 可能会对性能产生重大影响:kccoder.com/mysql/uuid-vs-int-insert-performance 像 Galera 这样的一些复制系统也需要一个。
  • @ceejayoz - 链接到一个糟糕的测试。没有一个 hist 表有 PRIMARY KEY,这对 InnoDB 来说是必不可少的。而且图表飞向天空的速度太快了。而且他没有说innodb_buffer_pool_size使用了什么值。但结论有些正确:在某些情况下,UUID 会降低性能。

标签: mysql indexing database-design primary-key


【解决方案1】:

纯粹根据你的表结构,我会说你的主键不正确。

相反,您的主要应该是:

PRIMARY KEY (user_id, address_type)

您是正确的,理想情况下每个表都应该有一个主键,但主键可以跨越多个字段。

有时将简单的自动递增 id 作为主键仍然更容易。 Innodb 存储引擎实际上会在一个不可见的字段中秘密地执行此操作。

也许在您的有限示例中不需要它,但在许多实际情况下,它可以使处理数据变得更容易。从这个意义上说,我想说的是,从学术角度来看,拥有一个人工自动递增的主键并不是最佳实践,但从“现实世界、运营和 MySQL 管理员”的角度来看,它可能是个好主意。

还有一些 ORM 系统只需要这个(虽然很糟糕)。

【讨论】:

  • 我猜你是对的。我编辑了我的示例表 - 它仍然是虚构的,但它确实代表了我在我的数据库中拥有的内容,现在您建议在 user_id 和 adress_type(name changed) 上使用 PK 确实很有意义。你说的,我之前说的,“没用的”PK表确实用在后台,挺有意思的。这实际上在我的示例中是否有效,或者我应该在user_id 列上添加INDEX?或者我应该两者兼得?如果我知道我会经常在查询的 WHERE 部分中使用纯非唯一 INDEX 列,我是否可以过度使用它?
  • @Zorann 我更改了主键以匹配您的新字段类型。您仍然不需要人工 PK,但在您的情况下,对 user_id 和 address_type 进行 PK 仍然有意义。如果有,则不需要在 user_id 上使用 INDEX,因为将使用 PK。
【解决方案2】:

从您的数据中可以明显看出,主键允许直接访问单行,没有任何问题或歧义..(特别是删除或更新)

这就是主键的具体用途..

你可能需要通过 user_id 将此表加入到其他表中

和 user_id 上的索引(不是唯一的)

create index  myidx on mytable(user_id)

对于更快的连接非常有用,只允许直接访问与单个 user_id 相关的行

【讨论】:

    【解决方案3】:

    关系数据库表确实需要主键。

    但这一切都归结为主键的定义。主键不一定是自动递增的单个整数列。

    主键是可以唯一标识每一行的任何列或多列集合。在您的情况下,user_idaddress_type 的组合可以做到这一点(正如 Evert 已经发布的那样)。

    所以如果你把你的桌子变成这样:

    CREATE TABLE user_address (
      user_id INT NOT NULL,
      address_type varchar(10) NOT NULL,
      address TEXT NOT NULL,
      PRIMARY KEY (user_id, address_type)
    );
    

    然后您可以像这样一次更新或删除一个特定行:

    UPDATE user_address SET ...
    WHERE user_id = ? AND address_type = ?;
    

    有些人认为强制执行一个约定更方便,即每个表都应该有一个整数列作为其主键。他们甚至可能坚持为了保持一致性,必须将该列称为id

    在一致性方面有一些优势,但另一方面,即使没有帮助,仍坚持该约定有点愚蠢。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-10
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      • 2011-09-21
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多