数据库主键设计之思考(转)

By | 07月23日
Advertisement

在我们的数据库设计中,不可逃避的就是数据库表的主键,可能有很多朋友没有深入思考过,主键的设计对整个数据库的设计影响很大,因此我们不得不要重视起来。

主键的必要性:

有些朋友可能不提倡数据库表必须要主键,但在我的思考中,觉得每个表都应该具有主键,不管是单主键还是双主键,主键的存在就代表着表结构的完整性, 表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,本记录的修改与删除,当我们没有主键时,这些操作会变的非常麻烦。

主键的无意义性:

我强调主键不应该具有实际的意义,这可能对于一些朋友来说不太认同,比如订单表吧,会有“订单编号”字段,而这个字段呢在业务实际中本身就是应该具 有唯一性,具有唯一标识记录的功能,但我是不推荐采用订单编号字段作为主键的,因为具有实际意义的字段,具有“意义更改”的可能性,比如订单编号在刚开始 的时候我们一切顺利,后来客户说“订单可以作废,并重新生成订单,而且订单号要保持原订单号一致”,这样原来的主键就面临危险了。因此,具有唯一性的实际 字段也代表可以作为主键。因此,我推荐是新设一个字段专门用为主键,此主键本身在业务逻辑上不体现,不具有实际意义。而这种主键在一定程序增加了复杂度, 所以要视实际系统的规模大小而定,对于小项目,以后扩展不会很大的话,也查允许用实际唯一的字段作主键的。

主键的选择

我们现在在思考一下,应该采用什么来作表的主键比较合理,申明一下,主键的设计没有一个定论,各人有各人的方法,哪怕同一个,在不同的项目中,也会采用不同的主键设计原则。

第一:编号作主键

此方法就是采用实际业务中的唯一字段的“编号”作为主键设计,这在小型的项目中是推荐这样做的,因为这可以使项目比较简单化,但在使用中却可能带来一些麻 烦,比如要进行“编号修改”时,可能要涉及到很多相关联的其他表,就象黎叔说的“后果很严重”;还有就是上面提到的“业务要求允许编号重复时”,我们再那 么先知,都无法知道业务将会修改成什么?

第二:自动编号主键

这种方法也是很多朋友在使用的,就是新建一个ID字段,自动增长,非常方便也满足主键的原则,优点是:数据库自动编号,速度快,而且是增量增长,聚集型主 键按顺序存放,对于检索非常有利;数字型的,占用空间小,易排序,在程序中传递也方便;如果通过非系统增加记录(比如手动录入,或是用其他工具直接在表里 插入新记录,或老系统数据导入)时,非常方便,不用担心主键重复问题。

缺点:其实缺点也就是来自其优点,就是因为自动增长,在手动要插入指定ID的记录时会显得麻烦,尤其是当系统与其他系统集成时,需要数据导入时,很难保证 原系统的ID不发生主键冲突(前提是老系统也是数字型的);如果其他系统主键不是数字型那就麻烦更大了,会导致修改主键数据类型了,这也会导致其他相关表 的修改,后果同样很严重;就算其他系统也是数字型的,在导入时,为了区分新老数据,可能想在老数据主键前统一加一个“o”(old)来表示这是老数据,那 么自动增长的数字型又面临一个挑战。

第三:Max加一

由于自动编号存在那些问题,所以有些朋友就采用自己生成,同样是数字型的,只是把自动增长去掉了,采用在Insert时,读取Max值后加一,这种方法可 以避免自动编号的问题,但也存在一个效率问题,如果记录非常大的话,那么Max()也会影响效率的;更严重的是并发性问题,如果同时有两人读到相同的 Max后,加一后插入的ID值会重复,这已经是有经验教训的了。

第四:自制加一

考虑Max加一的效率后,有人采用自制加一,也就是建一个特别的表,字段为:表名,当前序列值。这样在往表中插入值时,先从此表中找到相应表的最大值后加 一,进行插入,有人可能发现,也可能会存在并发处理,这个并发处理,我们可以采用lock线程的方式来避免,在生成此值的时,先Lock,取到值以后,再 unLock出来,这样不会有两人同时生成了。这比Max加一的速度要快多了。但同样存在一个问题:在与其他系统集成时,脱离了系统中的生成方法后,很麻 烦保证自制表中的最大值与导入后的保持一致,而且数字型都存在上面讲到的“o”老数据的导入问题。因此在“自制加一”中可以把主键设为字符型的。字符型的 自制加一我倒是蛮推荐的,应该字符型主键可以应付很多我们意想不到的情况。

第五:GUID主键

目前一个比较好的主键是采用GUID,当然我是推荐主键还是字符型的,但值由GUID生成,GUID是可以自动生成,也可以程序生成,而且键值不可能重 复,可以解决系统集成问题,几个系统的GUID值导到一起时,也不会发生重复,就算有“o”老数据也可以区分,而且效率很高,在.NET里可以直接使用 System.Guid.NewGuid()进行生成,在SQL里也可以使用 NewID()生成。

优点是:

同 IDENTITY 列相比,uniqueidentifier 列可以通过 NewID() 函数提前得知新增加的行 ID,为应用程序的后续处理提供了很大方便。

便于数据库移植,其它数据库中并不一定具有 IDENTITY 列,而 Guid 列可以作为字符型列转换到其它数据库中,同时将应用程序中产生的 GUID 值存入数据库,它不会对原有数据带来影响。

便于数据库初始化,如果应用程序要加载一些初始数据, IDENTITY 列的处理方式就比较麻烦,而 uniqueidentifier 列则无需任何处理,直接用 T-SQL 加载即可。

便于对某些对象或常量进行永久标识,如类的 ClassID,对象的实例标识,UDDI 中的联系人、服务接口、tModel标识定义等。

缺点是:

GUID 值较长,不容易记忆和输入,而且这个值是随机、无顺序的

GUID 的值有 16 个字节,与其它那些诸如 4 字节的整数相比要相对大一些。这意味着如果在数据库中使用uniqueidentifier 键,可能会带来两方面的消极影响:存储空间增大;索引时间较慢。

我也不是推荐GUID最好,其实在不同的情况,我们都可以采用上面的某一种方式,思考了一些利与弊,也方便大家在进行设计时参考。这些也只是我的一点思考而已,而且可能我知识面限制,会有一些误论在里面,希望大家有什么想法欢迎讨论。

Similar Posts:

  • 数据库主键设计之思考

    在我们的数据库设计中,不可逃避的就是数据库表的主键,可能有很多朋友没有深入思考过,主键的设计对整个数据库的设计影响很大,因此我们不得不要重视起来. 主键的必要性: 有些朋友可能不提倡数据库表必须要主键,但在我的思考中,觉得每个表都应该具有主键,不管是单主键还是双主键,主键的存在就代表着表结构的完整性,表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,本记录的修改与删除,当我们没有主键时,这些操作会变的非常麻烦. 主键的无意义性: 我强调主键不应该具有实际的意义,这可能对于一些朋友来

  • 数据库主键设计的思考

    在我们的数据库设计中,不可逃避的就是数据库表的主键,可能有很多朋友没有深入思考过,主键的设计对整个数据库的设计影响很大,因此我们不得不要重视起来. 主键的必要性: 有些朋友可能不提倡数据库表必须要主键,但在我的思考中,觉得每个表都应该具有主键,不管是单主键还是双主键,主键的存在就代表着表结构的完整性,表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,本记录的修改与删除,当我们没有主键时,这些操作会变的非常麻烦. 主键的无意义性: 我强调主键不应该具有实际的意义,这可能对于一些朋友来

  • 数据库主键设计思考-五大策略

    关于主键的故事,生动形象的描述了数据库中主键的作用,也部分阐述了各种不同数据库主键设计的优缺点,此处更详细的对数据主键的设计策略做引用总结,以供参考! 关于数据库中主键设计: 主键的必要性: 个人觉得每个表都应该具有主键,不管是单主键还是双主键,主键的存在就代表着表结构的完整性,表的记录必须得有唯一区分的标识,主键主要是用于作为其他表的外键关联.以及本记录的修改与删除操作,如果没有该对象的标识,这些操作会变的非常麻烦. 主键的无意义性: 这里强调主键不应该具有实际的意义,提出对象属性和对象标识都

  • [转]数据库主键设计文章绘粹

    在我们的数据库设计中,不可逃避的就是数据库表的主键,可能有很多朋友没有深入思考过,主键的设计对整个数据库的设计影响很大,因此我们不得不要重视起来. 主键的必要性: 有些朋友可能不提倡数据库表必须要主键,但在我的思考中,觉得每个表都应该具有主键,不管是单主键还是双主键,主键的存在就代表着表结构的完整性,表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,本记录的修改与删除,当我们没有主键时,这些操作会变的非常麻烦. 主键的无意义性: 我强调主键不应该具有实际的意义,这可能对于一些朋友来

  • MySQL数据库主键设计原则

    目录 1. 主键定义... 5 2. 主键设计原则... 5 2.1 确保主键的无意义性... 5 2.2 采用整型主键... 5 2.3 减少主键的变动... 5 2.4 避免重复使用主键... 6 2.5 主键字段定义区分... 6 3. 主键方案... 6 3.1 自增ID.. 6 3.2 UUID.. 7 3.3 ID物理主键+UUID逻辑主键... 7 4. 总结... 8 1. 主键定义 表中经常有一个列或多列的组合,其值能唯一地标识表中的每一行.这样的一列或多列称为表的主键,通过它

  • 数据库主键设置为全局唯一方案

    现在通过数据的水平切分(sharding)来实现数据库 Scale Out 的解决方案受到了越来越多人的青睐,但是在切分过程中可能遇到的问题也肯定不在少数,如切分规则的设计,切分后的访问路由,切分后的主键的全局唯一等等. 这里我主要列举几个可以使用在 MySQL 数据库主键全局唯一方案及其优劣,供大家参考: * 通过应用程序生成一个GUID,然后和数据一起插入切分后的集群.优点是维护简单,实现也容易.缺点是应用的计算成本较大,且GUID比较常,占用数据库存储空间较大,涉及到应用的开发. 说明:主

  • 数据库主键选取方式

    一.自动增长型字段 很多数据库设计者喜欢使用自动增长型字段,因为它使用简单.自动增长型字段允许我们在向数据库添加数据时,不考虑主键的取值,记录插入后,数据库系统会自动为其分配一个值,确保绝对不会出现重复.如果使用SQLServer数据库的话,我们还可以在记录插入后使用@@IDENTITY全局变量获取系统分配的主键键值. 尽管自动增长型字段会省掉我们很多繁琐的工作,但使用它也存在潜在的问题,那就是在数据缓冲模式下,很难预先填写主键与外键的值.假设有两张表: Order(OrderID, Order

  • 数据库主键自增插入显示值

    SQL Server 2008 数据库主键自增插入显示值 前几天在工作的时候遇到在删除数据库中表的数据的时候,删除之后,重新添加的数据没有得到原来的数据的id值(表中id为主键,且设置为自增) ,使用的是SQL Server 2008 ,现在已解决,和大家分享一下! 具体情况: 1.建立表t_test,设置主键自增,如下图 2.向表中插入数据 由于表中的主键字段id为自增在插入的时候不需要指定显示插入,所以Sql 语句为 <span style="font-size:24px;"

  • 解决SQL Server 2008数据库主键自增的问题

    1.原理实现 待补充. 2.设置主键自动增加 MS SQLServer 在MS SQLServer中,如果把表的主键设为identity类型,数据库就会自动为主键赋值. create table customers(id int identity(1,1) primary key not null, name varchar(15)); insert into customers(name) values('name1'),('name2'); select id from customers;

  • 从Oracle到SQL Server数据库主键的迁移

    由于项目需要要将以前Oracle的数据库转化为SQL Server,今天利用SQL Server的DTD进行数据库的迁移,但导入以后发现只导入了表结构和数据,而表的一些主键约束都没导过来,感觉很郁闷,而手头又没有好的迁移工具,如Erwin,所以动手写了个小工具,基本实现了主键的转移,主要代码如下: 主要控件: ADOConnOrcale: TADOConnection; //连接Oracle ADOConnSQLServer: TADOConnection; //连接SQL Server O1:

Tags: