逻辑数据库设计 - 需要ID(谈主键Id)

本文的目标就是要确认那些使用了主键,却混淆了主键的本质而造成的一种反模式。

一、确立主键规范

  每个了解数据库设计的人都知道,主键对于一张表来说是一个很重要,甚至必需的部分。这确实是事实,主键是好的数据库设计的一部分。主键是数据库确保数据行在整张表唯一性的保障。它是定位到一条记录并且确保不会重复存储的逻辑机制。主键也同时可以被外键引用来建立表与表之间的关系。

  难点是选择那一列作为主键。大多数表中的每个属性值都有可能被很多行使用。例如姓名,电子邮件地址等等都不能保证不会重复。

  在这样的表中,需要引入一个对于表的域模型无意义的新列来存储一个伪值。这一列被用作这张表的主键,从而通过它来确定表中的一条记录,即便其他的列允许出现适当的重复项。这种类型的主键列我们通常称其为伪主键或者代理键。

  大多数数据库提供一种和当前处理事务无关的底层方案,来确保每次都能生成全局唯一的一个整数作为伪主键,即使客户端此时正发起并发操作。

  主键存在的作用:

  1、确保一张表中的数据不会出现重复行。

  2、在查询中引用单独的一行记录。

  3、支持外键。

二、反模式:以不变应万变

  很多的书、文章以及程序框架都会告诉你,每个数据库的表都需要一个主键,且具有如下三个特性:

  (1)、主键的列名都叫做Id;

  (2)、数据类型是32位或者64位的整数

  (3)、主键的值是自动生成来确保唯一的。

  在每张表中都存在一个叫做Id的列是如此地平常,甚至Id已经成为了主键的同义词。很多程序员在一开始学习SQL时就被灌输了错误的概念。认为每张表都要增加一列Id,这显然太过于随意。

  1、冗余键值

  2、允许重复项

  一个组合键包含了多个不同的列,组合键的典型场景就是想上节中乱穿马路中的Contact表。主键需要确保一个给定的Product_Id和Account_Id的组合在整张表中只能出现一次,虽然同一个值可能在很多不同的配对出现。

  然而,当你使用了Id这一列作为主键,约束就不是Account_Id和Product_Id的组合必须唯一了。当你用这张交叉表去查询Account_Id和Product_Id的关系时,重复项会意料之外的结果。要确保没有重复项,你可以在Id之外,额外声明另外两列需要一个UNIQUE约束。但是当你在Account_Id和Product_Id这两列上应用了唯一性约束,Id这一列就会变成了多余的列。它已经背离了主键的初衷了。

  

  3、关键字意义不明确

  Id这个词是如此地普通,完全无法表达更深沉的意思,特别是在你做两张表连接查询,而他们都有一个叫做Id的主键时。

  select *
  from Accout as a
  join Bug b on (b.toId = a.Id)

  这种查询必须在查询时指定列别名,否则其中的一个Id列会覆盖掉另一列的Id的值。

  同时有两张表有相同的Id列的情况下也不能够使用using关键字。

  比如:SQL支持一种简洁的表达式来表示两张表联结(using)。如果两张表有同样的列名,就可以用如下的表达式来重写上面的需求。

select * from Account join Bug using(Bug_id);  --一个主键,一个外键。

  然而,如果所有的表都要求定义一个叫做id的伪主键,那么将不能使用这种简写方式。

  4、使用组合键

  一些开发人员觉得组合键太难使用,如果要比较两个键值,必须比较其包含的所有列的值;一个引用组合键的外键,其本身也必须是一个组合外键。此外,使用组合键需要打更多的字。其实这是不对的。组合键的适当的时候是应该被使用的。

三、识别反模式

  1、我觉得这张表不需要主键。

  这么说的人一定是误解了“主键”和“伪主键”的含义,每张表都必须要有一个主键,这个是毫无疑问的。实际上可能这个人需要的是一个组合键,或者一个更自然的列名来做主键。

  2、我怎么能在多对多的表中存储重复的项?

  在一个对多对关系的交叉表中需要声明一个主键约束。或者至少需要有一个针对那些被引用作为外键的唯一约束。

四、合理使用发模式

  使用伪主键,或者通过自动增长的整型的机制本身没有什么错误,但不是每张表都需要一个伪主键,更没有必要将每个伪主键都定义成Id。

  对于太长而不方便实现的自然键来说,伪主键是一个很好的代替品。比如在一个记录文件系统的所有文件属性的表中,文件路径是一个很好的自然键,但对一个字符串列做索引的开销会很大。

五、解决方案:裁剪设计

  主键是约束而非数据类型。你可以定义任意列或任意多个的列作为主键。只要其数据类型支持索引。另外在此必须要补充的是,在SqlServer中,主键和聚集索引并没必然的关系。SqlServer只是默认将聚集索引建在主键上,实际上你完全可以将聚集索引手动定义到非主键列。

  1、为主键选择更有意义的名称

  比如为Product这张表的主键应该叫做Product_Id。

  2、外键应该尽可能地和所引用的列使用相同的名称,这通常意味着:一个主键的名称应该在整个数据库的设计中唯一;任意两张表都不应该使用相同的名称来定义主键,除非其中之一引用了另外一个作为外键。然而凡是都有例外,又是外键的名称需要和其所引用的主键区分开,从而使的它们之间的引用关系表现得更加清晰。

  比如在一张外键表中将外键声明为create_by(由谁创建)。  

规则 自然键 代理键
主键必须唯一的识别每一记录 但与输入和人为错误有关 系统自生成的数据是唯一的
一个记录的主键不能为空 只有数据可知时才能输入记录 当记录生成时才被系统建立
当生成记录时,主键的值必须存在 只有数据可知时才能输入记录 当记录生成时才被系统建立当记录生成时才被系统建立
主键必须保持稳定——你不能更改主键的域 自然键与一些商业规则和其他外部影响有关 代理键对程序功能和数据保持中立
主键必须简洁,不要包含过分的属性 一个自然键可以包含多个域 代理键只能包含多个域
主键的值不能改变 自然键通常改变 代理键通常不更改

六、拥抱自然键和组合键

  如果你的表中包含一列能够确保唯一、非空以及能够用来定位一条记录,就别仅仅因为传统而觉得有必要再加上一个伪主键。

  实践证明,一张表中的每一列都在最初的设计之后遭遇改变是很正常的事情。数据库的设计趋向于在整个项目的声明周期中不断地调整和优化,并且决策者也可能一点也不在乎自然键的“神圣不可侵犯”。有时候一个列最开始是像是个很好的自然主键,但随后有允许合法的重复项。此时伪主键便成了唯一的选择。

  在合适的时候也可以使用联合主键,比如一条记录可以通过多个列的组合完全定位。就像上面提到的Contact表,那就通过那些列创建一个联合主键吧。

  总结:我个人的见解就是能用代理键就尽量用代理键。除非代理键真的非常多余,就好似上面的组合键代替复合键的例子一样。

转自:https://www.cnblogs.com/fuqiang88/articles/3817301.html

数据库设计时,每个表要不要都设置自增主键ID!(转)的更多相关文章

  1. Mysql数据库表的自增主键ID号乱了,需要重新排列。

    Mysql数据库表的自增主键ID号乱了,需要重新排列. 原理:删除原有的自增ID,重新建立新的自增ID. 1,删除原有主键:ALTER TABLE `table_name` DROP `id`; 2, ...

  2. mybatis用mysql数据库自增主键,插入一条记录返回新增记录的自增主键ID

    今天在敲代码的时候遇到一个问题,就是往数据库里插入一条记录后需要返回这个新增记录的ID(自增主键), 公司框架用的是mybatis的通用Mapper接口,里面的插入方法貌似是不能把新纪录的ID回填到对 ...

  3. Mybatis+Mysql插入数据库返回自增主键id值的三种方法

    一.场景: 插入数据库的值需要立即得到返回的主键id进行下一步程序操作 二.解决方法: 第一种:使用通用mapper的插入方法 Mapper.insertSelective(record): 此方法: ...

  4. oracle数据库建表设置自增主键

    create sequence userlogin_ID increment by 1 start with 1 minvalue 1 maxvalue 9999999999999999 nocach ...

  5. 数据库插入数据返回当前自增主键ID值的方法

    当我们插入一条数据的时候,我们很多时候都想立刻获取当前插入的主键值返回以做它用.我们通常的做法有如下几种: 1. 先 select max(id) +1 ,然后将+1后的值作为主键插入数据库: 2. ...

  6. mysql - 在已有真实数据的表的基础上加入自增主键

    先删除自增长在删除主键Alter table tb change id id int(10);//删除自增长Alter table tb drop primary key;//删除主建 然后再常规添加 ...

  7. oracle使用execute immediate方式完成函数动态传入表名并操作 返回新的主键id值

    CREATE OR REPLACE FUNCTION SEQ1 (v_bname in VARCHAR2) return NUMBER is v_bcount NUMBER; BEGIN execut ...

  8. mysql数据库表的自增主键号不规律,重新排列

    mysql数据库表的自增主键ID乱了,需要重新排序. 原理:删除原有的自增ID,重新建立新的自增ID. 1.删除原有主键: ALTER TABLE `table_name` DROP `id`; 2. ...

  9. MySQL导入csv文件内容到Table及数据库的自增主键设置

    写在前面 目的是测试将csv文件内容导入到表中, 同时记录一下自增主键的设置. 测试采用MySQL8.0. 新建表customer_info如下, 未设置主键. 修改上表, 添加主键id, 并设置为自 ...

随机推荐

  1. Cassandra 数据一致性

    基本概念 一致性: 在分布式系统中的所有数据备份,在同一时刻是否同样的值 QUORUM: 是一个可以计算的数字,对数据一致性起重要作用 Node: 主要用来存储数据 Data Center: 数据中心 ...

  2. XML 浏览器支持

    几乎所有的主流浏览器均支持 XML 和 XSLT. Mozilla Firefox 从 1.0.2 版本开始,Firefox 就已开始支持 XML 和 XSLT(包括 CSS). Mozilla Mo ...

  3. MongoDB基本操作(增删改查)

    基本操作      基本的“增删查改“,在DOS环境下输入mongo命令打开shell,其实这个shell就是mongodb的客户端,同时也是一个js的编译器,默认连接的是“test”数据库.  

  4. 洛谷P2482 [SDOI2010]猪国杀——题解

    猪国杀,模拟题的一颗耀眼的明珠,成长大牛.锻炼码力必写题! 模拟题没什么思维难度.只要按部就班地去做就是.模拟简单在这,难也在这.因为题面巨长,条件巨多,忽疏一点都有可能全盘皆输.故推荐考试时碰见了, ...

  5. You have 1 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): shopadmin. Run 'python manage.py migrate' to apply them.

    数据库迁移时报错, You have 1 unapplied migration(s). Your project may not work properly until you apply the ...

  6. redis 安装 主从同步 哨兵模式

    一.redis 的安装1.先将安装包放到linux的一个文件夹下面 2.解压压缩包如图所示 3.解压后进入解压文件 4.安装: make 出现it.s a good idea to run 'make ...

  7. php 防盗链

    防盗链的技术已经很普遍了,有些网站不喜欢自己的图片被别的网站直接复制使用,便使用了防盗链的技术,这样别人在直接复制使用网站图片时,图片便会按照程序的设定不显示或显示防盗链等字样. 使用了防盗链技术,不 ...

  8. post与get方法的区别

    1. GET请求:请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接.URL的编码格式采用的是ASCII编码,而不是unicode,即是说所有的非ASCII字符都要编码之 ...

  9. eclipse有时候导入了包,但是还是有红线,找不到

    clean一下工程,选择project->clean->clean project selected blow并且选择build only the selected project

  10. Git-Runoob:Git 分支管理

    ylbtech-Git-Runoob:Git 分支管理 1.返回顶部 1. Git 分支管理 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同 ...