如何避免主键错误

How to avoid mistakes at Primary Key

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

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

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

I was thinking to add idcustomer as auto_increment but I am not sure that will be a great idea.

确实是个好主意

您的其他列(属性)不一定具有唯一值。换句话说,它们不是 suitable 用作 natural 主键。什么样的值可以作为自然主键?可能是员工编号。 产品序列号可能有效。纳税人 ID 号(社会保险号)不起作用:数量惊人的人错误地使用了重复号码。选择一个真实世界的项目作为主键的唯一性标准是如此之高,以至于大多数数据库设计者甚至都不会尝试。

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

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

例如,您可能有销售额 table。 table 可能有这些列:

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 and foreign keys。在 "logical database design," 的行话中,您可以阅读有关 entities(客户、销售)和 relationships 的内容。每个 table 都有自己的一系列自动递增值。

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

 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 设置为每个 table 中的第一列也是一种约定。

惯例很好:它们可以帮助下一个人查看您的申请。他们也帮助你未来的自己。

注意:我的销售额 table 只是一个示例,用于说明自动递增 id 值可能有何用处。我不认为这是真实世界销售的好布局 table:它不是。

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

  • non null -(保证每一行的所有 PK 列都具有非 NULL 值)
  • 唯一 -(没有两行 ever 具有相同的值集。ever
  • 简单 -(单列,本机数据类型)
  • 短-(簇键将在每个二级索引和外键中重复)
  • immutable - (一旦赋值,该值将不会改变)
  • 匿名-(不携带任何有意义的信息)

我们可以就这些属性中的每一个、含义和优点以及不具有这些属性的主键的缺点发表意见并进行讨论。但很多最终都是关于什么是最重要的,什么是根本不重要的意见。)

我有理由认为这些属性中的每一个都是可取的。我承认其他人不持有相同的意见。

如果这个列表有效,那么 surrogate 主键可以满足所有这些。

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

 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 方面不是完美的功能。)


重要

断言使用代理主键是正确的方法,或者说是唯一的方法。

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

但我要指出(最后)当事实证明(项目后期,新发现的需求)选择的自然键不是满足我列出的 "desirable properties" 中的一个(或多个)

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

最安全的方法是在每个 table 上创建一个名为 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您可以创建复合聚集索引,它是具有多个列的主键。如果您确定 table 永远不会变得巨大,并且您将定期访问 table 使用复杂的过滤器(指定该键中最左边的列子集),这可能是一种优化.不过我不会使用这种方法。

不过,

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

的隐藏列

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