【问题标题】:How to avoid mistakes at Primary Key如何避免主键错误
【发布时间】:2018-10-29 10:31:00
【问题描述】:

您好,我是数据库的初学者,因此我想问您应该使用哪些属性作为主键以避免错误:

    CREATE TABLE customer(
    name
    first_lastname
    street
    ZIP_code
    mobile_phone
    telephone
    email
    gender
    birthdate
    nationality);

我曾考虑将 idcustomer 添加为 auto_increment,但我不确定这是否是个好主意。

【问题讨论】:

    标签: mysql sql database-design primary-key surrogate-key


    【解决方案1】:

    我正在考虑将 idcustomer 添加为 auto_increment,但我不确定这是否是个好主意。

    确实是个好主意。

    您的其他列(属性)不一定具有唯一值。换句话说,它们不适合用作自然主键。什么样的值可以作为自然主键?可能是员工编号。 产品序列号可能会起作用。纳税人身份证号码(社会安全号码)不起作用:令人惊讶的人数错误地使用了重复的号码。选择真实世界项目作为主键的唯一性标准非常高,以至于大多数数据库设计人员甚至都不会尝试。

    所以创建一个保证唯一的主键通常是好的设计。这种键的行话是 surrogate 主键。大多数 DBMS 系统,包括 MySQL,都为此提供了自动递增的数字。

    您可以选择两种约定之一来命名id 值。一种是叫它id。另一种叫customer_id(表名加上_id)。当您开始在其他表中使用这些值来建立关系时,第二个将帮助您保持直截了当。

    例如,您可能有一张销售表。该表可能包含以下列:

    sales_id      autoincrementing pk
    customer_id   the id of the customer to whom the sale was made. (foreign key)
    item_sold     description of the item
    list_price
    discount
    net_price
    

    你明白了。阅读primary keys 和foreign keys。在“逻辑数据库设计”的行话中,您可以阅读实体(客户、销售)和关系。每个表都有自己的一系列自动递增值。

    然后您可以使用这样的查询来找出每个客户的销售额。

     SELECT customer.name, customer.first_lastname,
            COUNT(sales.sales_id) number_of_sales,
            SUM(sales.net_price) revenue
       FROM customer
       JOIN sales ON customer.customer_id = sales.customer_id
      GROUP BY customer.customer_id, customer.name, customer.first_lastname
    

    这里sales 实体 与customer 实体 具有多对一关系。这是通过在每个sales 行中指向客户的customer_id 属性来实现的。

    将 id 设置为每个表中的第一列也是一种约定。

    约定很好:它们可以帮助下一个人查看您的应用程序。它们还可以帮助您未来的自己。

    注意:我的 sales 表只是一个示例,展示了自动递增 id 值如何可能有用。我并没有声称它对于现实世界的销售表来说是一个好的布局:它不是。

    【讨论】:

    • 谢谢,但如果我有其他表,如销售,则具有整数 auto_increment。这还是个好主意吗?
    • @apk 如果您已经有其他具有整数 auto_increment 的表也没关系。每个表的 auto_increment 列都是独立的。
    • 谢谢,这意味着我可以在其他表上做同样的事情,比如下放和其他表。
    • 没错。典型业务数据库中的大多数数据表都有自己的自动递增主键。
    • 确实可以将任何有保证的唯一值(如 UUID)用作 pk。但是:许多现代索引方案允许表在以升序插入新值时更好地扩展,而不是按本质上的随机顺序。为什么?索引碎片。
    【解决方案2】:

    PRIMARY KEY 有几个理想的属性(其中一些非常明显,但我们将列举它们)

    • 非空 - (保证每一行的所有 PK 列都有一个非空值)
    • 唯一 - (没有两行永远具有相同的一组值。永远)
    • 简单 -(单列,本机数据类型)
    • short - (集群键将在每个二级索引和外键中重复)
    • 不可变——(一旦赋值,值不会改变)
    • 匿名 -(不携带任何有意义的信息)

    我们可以就这些属性中的每一个、含义和好处以及不具有这些属性的主键的缺点进行讨论。但是很多人最终都会对什么是最重要的,什么是不重要的。)

    我有理由认为这些属性中的每一个都是可取的。而且我承认其他人的观点不同。

    如果此列表有效,则 代理 主键可以适合所有这些。

    在 MySQL 中,实现 代理 主键的一种可能方法是在表中添加一个额外的列:

     CREATE TABLE mytable 
     ( id                INT NOT NULL AUTO_INCREMENT PRIMARY KEY  COMMENT 'PK'
     , cust_email        VARCHAR(255) NOT NULL                    COMMENT 'UX1'
     , cust_name_title
     , cust_name_first
     , cust_name_last
     , cust_name_suffix
     , cust_addr_street
     , cust_addr_line2
     , cust_addr_city
     , cust_addr_state
     , cust_addr_postal_code
     , UNIQUE KEY customer_UX1 (cust_email) 
     )
    

    请注意,使用 AUTO_INCREMENT 不是要求。这是许多人认为有用且易于使用的功能。 (关于 AUTO_INCREMENT 的一些细节使其在 PRIMARY KEY 方面不够完美。)


    重要

    我确实不断言使用代理主键是正确的方法,或唯一的方法。

    代理主键不是成功的数据库实施项目的要求。许多成功的项目都是使用自然键实现的。

    但我会指出(最后)当事实证明(在项目后期,新发现的要求)选择的自然键结果不满足自然键时,一些坚定的自然键信徒已经被严重烧毁(或更多)我列出的“理想属性”。

    【讨论】:

      【解决方案3】:

      令人惊讶的是,到目前为止,没有一个答案涉及您的业务需求。您是否了解您的业务流程、与客户发生的交互以及如何在业务领域中识别客户?标识属性(例如,在电子商务应用程序中可能是登录名)通常应该是表中的键。除非您了解该键的用途,否则仅添加自动增量并不是正确的做法。

      【讨论】:

      • 这是一个传统的商店,没有在线
      • @apk,无论是哪种商店,了解业务需求和“客户旅程”都是确定关键的重要部分。如果您在店内收集有关客户出生日期和国籍的信息,那么我想商店必须有客户注册流程。如果客户正在注册一项服务,那么我预计商店可能必须为他们分配一个标识符,他们使用该标识符与该服务进行交互。例如,标识符可能是电子邮件地址,但我们无法从您在问题中告诉我们的内容中判断出来。
      【解决方案4】:

      主键是唯一标识表中行的一列或一组列。考虑到这一点,您可以将任何列唯一标识customer 行作为主键。您可以使用电话号码或名字、姓氏和电话号码的组合作为主键。但更被接受的方法是添加一个额外的列,可能像你想象的那样命名为idcustomer 或customer_id 或只是id,这对于每个客户来说都是唯一的,并使其成为主键;使这个整数列auto_increment 是个好主意。

      【讨论】:

        【解决方案5】:

        最安全的方法是在每个表上创建一个名为id 的PK 列。不要成为英雄,只是去一个未签名的 bigint。 PK 溢出,无论多么不可能,都不是您想要的问题。

        您可以使用: id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY

        或者用SERIAL关键字替换中间位,这是BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE的别名

        请记住,如果您使用基于语句的复制,AUTO_INCREMENT 可能会导致问题。基于语句的复制是 5.7.6 之前的默认设置。

        使用合成键将您正在建模的对象的特征与该对象的唯一标识符分离,如果您需要更改架构,这很方便。更改 MySQL PK 的成本很高。它还保证您将拥有一个唯一的非空列,用于引用外键。此外,一些 ORM 期望有一个 id PK 列——如果你喜欢这种东西的话。

        使用 MySQL,您可以创建复合聚集索引,它是具有多个列的主键。如果您确定该表永远不会变得巨大,并且您将定期使用复杂过滤器访问该表,这些过滤器指定该键中列的最左侧子集,这可能是一种优化。不过我不会使用这种方法。

        不过,InnoDB 表需要一个主键。即使您没有显式创建一个,数据库也会隐式选择它找到的第一个 UNIQUE 列。如果没有,它将创建一个名为 GEN_CLUST_INDEX 的隐藏列。

        【讨论】:

          猜你喜欢
          • 2019-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-05
          • 1970-01-01
          • 2021-11-19
          • 2018-07-04
          相关资源
          最近更新 更多