主键应该是auto_increment吗?

时间:2014-04-20 08:34:37

标签: database database-design

我知道在设计表时使用主键更好。

但我想知道何时设计主键,需要设置auto_increment?

如果完成,有什么好处?

我听,这可以保持b-tree的稳定,但我不知道为什么?

如果table有一个唯一列,那么将唯一列设置为主键或添加新列'id'作为auto_increment主键更好?

你能帮帮我吗?感谢。

4 个答案:

答案 0 :(得分:6)

  

我知道在设计表时使用主键会更好。

实际上,无密钥表是multiset(因为它允许重复),因此不是严格意义上的关系(这是一个集合),因此您的数据库实际上不会是"关系"

请注意"主要" (PRIMARY KEY)和" alternate" (UNIQUE约束)键是logically equivalent

  

但我想知道何时设计主键,需要设置auto_increment?

您实际上是在问多个问题:

  1. 我应该创建一个密钥吗?
  2. 如果是,我应该创建一个代理键吗?
  3. 如果是,应该是整数吗?
  4. 如果是,我应该自动增加吗?
  5. (1)的答案是"几乎总是"。当数据不重要时,有一些非常罕见的情况。出于性能原因你可能会跳过它,但这种情况非常罕见。

    (2)的答案是"它取决于" - 可以找到主要的利弊here

    (3)的答案取决于您是否需要从数据库中独立生成(例如,在断开连接时,或在连接到其他数据库时)。如果是,您可以使用GUID(显然不能自动递增,但可以独立生成)。如果不是,那么你可以使用整数 - 它们更紧凑,通常更快。

    最后,如果你达到(4),那么你几乎肯定想让它自动递增,原因如下所述。

      

    如果完成,有什么好处?

    • 使整数代理键自动递增的主要好处是多个并发客户端永远不会收到相同的生成值。如果您只是尝试SELECT MAX(ID) + 1 FROM ...,则无法保证其他客户端不会同时尝试执行相同的操作,并最终会产生相同的结果(随后导致密钥违规)。
    • 另一个好处是DBMS将使用高度优化的代码路径来生成新的唯一值。
    • 缺点是自动增量机制通常事务感知:如果生成新的ID值然后ROLLBACK事务,则不会再次生成该值。话虽这么说,代理密钥没有任何意义(如果他们有,他们不会代理),所以这样的"漏洞"是无关紧要的。
      

    如果表格具有唯一列,那么将唯一列设置为主键或添加新列“id' as auto_increment主键?

    如果属性在"逻辑级别"本质上是唯一的,那么相应的表列必须是唯一的(通过PRIMARY KEY或UNIQUE约束),无论您以后决定添加代理密钥。

答案 1 :(得分:5)

  

我想知道何时设计主键,需要设置auto_increment?

不,这不是绝对必要的。有时自然键很好。

  

如果完成,有什么好处?

使用自动增量代理键的优点:

  • 代理键永远不需要更改,即使表中的所有其他列都可以更改。
  • 当您同时插入多个用户时,RDBMS更容易确保自动增量键的唯一性,而无需锁定和没有竞争条件。
  • 使用整数是可用于主键的最紧凑的数据类型,因此它导致的索引比使用长字符串的索引小。例如。
  • 插入B树索引的效率(见下文)。
  • 当唯一的其他候选键由多列组成时,使用单列而不是多列引用行更简单,更整洁。

使用自然键的优点:

  • 该列对实体有一些含义,例如电话号码。您无需为代理键存储额外的列。
  • 使用外键引用自然主键的其他表获得有意义的值,因此可以避免连接。例如,如果您想获取颜色名称,则shoes引用colors的表格需要进行联接。但是,如果您使用颜色名称​​作为 colors的主键,则该值已经成为shoes表的一部分。

不需要代理自动增量键的其他情况:

  • 您已经有了其他列的组合(无论是代理键还是自然键),它们为表提供候选键。在多对多表中找到了一个很好的例子。如果表将电影映射到演员,即使主键引用了电影和演员,那么您已经拥有这两列的候选键,而您不需要另一个自动增量列。
  

我听,这可以保持b-tree的稳定,但我不知道为什么?

将值插入B树中间的任意位置可能会导致索引重组成本高昂。

这里有一个动画示例:http://www.bluerwhite.org/btree/

查看示例“将密钥33插入B树(带拆分)”,其中显示了将值插入到溢出它的B树节点中的步骤,以及B树响应的内容

现在想象一下,示例说明只显示了更深层的B树的底部(就像索引B树有数百万个条目的情况一样),填充父节点本身就可以了溢出,并强制拆分操作继续向上树中的更高级别。如果已经填充了树顶部的所有祖先节点,这可以一直持续到树的最顶层。

由于节点分裂并且必须重新构建,它们可能需要更多空间,但它们存储在数据库文件的某些页面上,其中没有空余空间。因此,存储引擎必须将索引的某些部分重定位到文件的另一部分,并且可能只针对单个INSERT重写大量索引页。

自动增量值自然总是插在B树的最右边。正如@ BrankoDimitrijevic在下面的评论中指出的那样,这并不会降低它们导致如此费力的节点分裂和重组到索引的可能性。但是B树实现代码可以通过其他方式针对这种情况进行优化,有些则可以。

  

如果table有一个唯一列,那么将唯一列设置为主键或将新列'id'添加为auto_increment主键更好?

如果唯一列也是非可空的,则可以将其用作主键。主键要求所有列都不可为空。

答案 2 :(得分:0)

拥有一个自动增量PK可以轻松创建一个永远不需要更改的密钥,从而可以轻松地在其他表中引用。

如果您的数据具有唯一且无法更改的自然列,则您也可以使用它们。提醒你大多数事情永远不会改变"如果有足够的时间,就像某人的社会保障号码那样,往往会这样做......

为简单起见,我总是使用PK的自动增量(标识)列。

答案 3 :(得分:-1)

支持在表定义中使用代理键

感谢@Branko Dimitrijevic通过描述SURROGATE KEYS的角色并进入讨论的中心来打开关系数据库主键(PK' s)的主题。根据定义,代理键除了它们在表中每个记录中的值之间的唯一性之外,没有任何内在含义。

还要感谢@Mattias Åslund您的额外智慧:

  

请注意大多数事情"永远不会改变"如果有足够的时间,就像某人的社会保障号码那样,往往会这样做......

我补充说,即使选择的值为"不可更改"实际上并没有改变,支持的业务或组织本身的规则也可能随着时间的推移而变化,从而可能影响给定设计的核心假设。

有关跟踪个人的人口统计和生物识别关键值整合的有用讨论,请参阅计算机专业人员社会责任提出的Choosing an Appropriate Key for New Databases部分。

备用唯一数据库密钥的案例:样本架构

我打算通过围绕特定示例设计的讨论来处理这篇文章的评论,以解释分配不是代理的主键时可能出错的事情类型键。其中许多假设来自实际应用的观察。由于其他系统和数据源依赖于其假设而将其设计引入其他业务流程的复杂性,因此很好地记住了它们。

设计和样本数据如下,从臭名昭着的臭名昭着的Scott / TIGER数据库设计中借用。

SQL Fiddle

MySQL 5.5.32架构设置

CREATE TABLE employee 
    (
     fake_ssn  varchar(15) primary key,
     last_name varchar(40),
     first_name varchar(40),
     dept_id  varchar(15),
     hire_date  date,
     salary int,
     email varchar(100)
    );

INSERT INTO employee
(fake_ssn, last_name, first_name, dept_id, hire_date, salary,
 email)
VALUES
('130-60-0101', 'MARLOWE', 'JACOB', '1200-05', date('2009/01/25'),
 8000, 'jacob@some-company.com'),
('967-22-5025', 'CRACHITT', 'BOB', '1200-05', date('2010/02/05'),
 500, 'bobc@some-company.com'),
('040-36-5555', 'PERRY', 'VICTORIA', '1200-02', date('2011/05/25'),
 2700, 'vperry@some-company.com'),
('203-89-1010', 'STEVENS', 'KEVIN', '2955-03', date('2007/04/25'),
 1800, 'kevin.stevens@some-company.com'),
('409-99-1111', 'MCLANE', 'JONATHAN', '2955-03', date('2009/03/02'),
 4200, 'jon.j.mclane@some-company.com');

CREATE TABLE department
    (
     dept_id  varchar(15) primary key,
     dept_manager varchar(40),
     dept_title varchar(40)
    );

INSERT INTO department
(dept_id, dept_manager, dept_title)
VALUES
('1200-05', 'MARLOWE', 'FINANCE'),
('1200-02', null, 'HR'),
('2955-03', 'JOHNM', 'MARKETING');

COMMIT;

查询1

SELECT fake_ssn, last_name, first_name, dept_id, hire_date,
   salary, email    
FROM employee

<强> Results

|    FAKE_SSN | LAST_NAME | FIRST_NAME | DEPT_ID |                       HIRE_DATE | SALARY |                          EMAIL |
|-------------|-----------|------------|---------|---------------------------------|--------|--------------------------------|
| 040-36-5555 |     PERRY |   VICTORIA | 1200-02 |      May, 25 2011 00:00:00+0000 |   2700 |        vperry@some-company.com |
| 130-60-0101 |   MARLOWE |      JACOB | 1200-05 |  January, 25 2009 00:00:00+0000 |   8000 |         jacob@some-company.com |
| 203-89-1010 |   STEVENS |      KEVIN | 2955-03 |    April, 25 2007 00:00:00+0000 |   1800 | kevin.stevens@some-company.com |
| 409-99-1111 |    MCLANE |   JONATHAN | 2955-03 |    March, 02 2009 00:00:00+0000 |   4200 |  jon.j.mclane@some-company.com |
| 967-22-5025 |  CRACHITT |        BOB | 1200-05 | February, 05 2010 00:00:00+0000 |    500 |          bobc@some-company.com |

查询2

SELECT dept_id, dept_manager, dept_title    
FROM department

<强> Results

| DEPT_ID | DEPT_MANAGER | DEPT_TITLE |
|---------|--------------|------------|
| 1200-02 |       (null) |         HR |
| 1200-05 |      MARLOWE |    FINANCE |
| 2955-03 |        JOHNM |  MARKETING |

假社会安全号码

FAKE名称只是提醒您这些都是随机生成的值。)虽然这通常是一种流行的&#34; unqiue&#34;根据美国社会保障局的数据,人员记录和数据库的价值并不是唯一的。这也是有问题的,因为这个价值及其转让受到最近通过的隐私法的严格监管。

名称组合

即使使用包含中间首字母创建的其他组合,不知何故仍然有太多同名的人。看看社会保障管理局对于2012年出生的婴儿的注册名称所说的话:

SS Administration Popular Baby Names

从现在开始的二十年,当2012年 JACOB SOPHIA 从学校毕业时,他们将涌入数千人的劳动力队伍中其他人喜欢他们......

按婚姻或法律原因进行的名称更改也会依赖其作为业务键值的数据库记录的参照完整性受到威胁。

部门ID

有些公司会尝试从其他值派生密钥以生成SMART KEYS。在实践中观察这些类型的钥匙根本不聪明。示例中的值:1200-021200-052955-03旨在类似于&#34;智能密钥&#34;。第一个值可能是公司园区或多地点业务的街道地址或建筑物编号。第二个值(&#34; -02&#34;,&#34; -03&#34;,&#34; -05&#34;)可能是部门所在建筑物的楼层。

更改建筑物,移动部门或完全重新定位业务会使DEPARTMENT ID的这种位置依赖性变得无用。

部门经理

这个很微妙,但这种关系连接存在漏洞。 MANAGER也是一名员工,它使EMPLOYEEDEPARTMENT之间的关系联接成为循环联盟:

  • MANAGER(来自DEPARTMENT)是EMPLOYEE表上的外键约束,还是
  • DEPT_ID(FROM EMPLOYEE)是DEPARTMENT表上的外键约束吗?

如果您放弃MANAGEREMPLOYEELAST_NAMEFIRST_NAME + LAST_NAME)上的某个关键列之间的外键约束,则可能存在非法风险MANAGER的统一值。

......看着

<强> Query of The DEPARTMENT Table

| DEPT_ID | DEPT_MANAGER | DEPT_TITLE |
|---------|--------------|------------|
| 1200-02 |       (null) |         HR |
| 1200-05 |      MARLOWE |    FINANCE |
| 2955-03 |        JOHNM |  MARKETING |

DEPT_MANAGER表中DEPARTMENT的错位,因为部门经理的名称有三种不同的表示方式:none(null),ALL-CAPS姓氏,ALL-CAPS First姓名,最后一个姓名。

结论

从这篇文章中得出的一个重要教训是,通过整合派生值,使密钥更多而不是密钥,根据业务规则的假设创建值会限制数据库设计的灵活性,因为如果业务规则发生更改,则主键或连接键值等值也会更改。

  

作为业务应用程序的开发人员或维护人员,如果您已控制并拥有代表业务应用程序本身内部结构的部分,则您(或您的团队)能够更好地支持当前的业务条件。主键实际上可能永远不会出现在客户或面向用户的情况中,但它应该受到保护,以便它所代表的关系不会随着时间而改变。

特别感谢:

来自2012年热门婴儿名字页面的图片来源:

http://www.ssa.gov/OACT/babynames/#ht=0