• 数据库主键


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

    主键的必要性 :

    在有些数据库中,虽然主键不是必需的,但最好为每个表都设置一个主键,不管是单主键还是复合主键。它存在代表着表结构的完整性,表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,以及本记录的修改与删除。

    主键的无意义性 

    在开发过程中,读者可能会看到将一些表使用有意义的字段表示主键,例如“用户登录信息表”将“登录名”(英文名)作为主键,“订单表”中将“订单编号”作为主键,如此设计主键一般都是没什么问题,因为将这些主键基本不具有“意义更改”的可能性。但是,也有一些例外的情况,例如“订单表”需要支持需求“订单可以作废,并重新生成订单,而且订单号要保持原订单号一致”,那将“订单编号”作为主键就满足不了要求了。因此读者在使用具有实际意义的字段作为主键时,需要考虑是否存在这种可能性。

    主键的选择

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

    第一:编号作主键

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

    第二:自动编号主键

    这种方法也是很多朋友在使用的,就是新建一个 ID 字段,自动增长,非常方便也满足主键的原则,

    优点是

      数据库自动编号,速度快,而且是增量增长,聚集型主键按顺序存放,对于检索非常有利 ; 

      数字型的,占用空间小,易排序,在程序中传递也方便 ; 

      如果通过非系统增加记录(比如手动录入,或是用其他工具直接在表里插入新记录,或老系统数据导入)时,非常方便,不用担心主键重复问题。

    缺点是

      其实缺点也就是来自其优点,就是因为自动增长,在手动要插入指定 ID 的记录时会显得麻烦,尤其是当系统与其他系统集成时,需要数据导入时,很难保证原系统的 ID 不发生主键冲突(前提是老系统也是数字型的) ; 

      如果其他系统主键不是数字型那就麻烦更大了,会导致修改主键数据类型了,这也会导致其他相关表的修改,后果同样很严重 ; 

      就算其他系统也是数字型的,在导入时,为了区分新老数据,可能想在老数据主键前统一加一个“ o ”(old)来表示这是老数据,那么自动增长的数字型又面临一个挑战。

      MySQLauto_increment)、SQL ServerIDENTITY)、InformixOracle(首先创建自增序列,接着为自增主键的表创建插入时的触发器,给自增主键ID赋值)等数据库都支持这种自增主键,这种主键在各种系统中应用广泛,但是如果考虑到有新旧系统并存等问题,为了避免不必要的麻烦,使用自增主键要三思。

     

    第三: Max 加一

    优点是

      由于自动编号存在那些问题,所以有些朋友就采用自己生成,同样是数字型的,只是把自动增长去掉了,

      采用在Insert 时,读取 Max 值后加一,这种方法可以避免自动编号的问题,

    缺点是

      但也存在一个效率问题,如果记录非常大的话,那么Max() 也会影响效率的 ;

      更严重的是并发性问题,如果同时有两人读到相同的 Max 后,加一后插入的 ID 值会重复,这已经是有经验教训的了。

    第四:自制加一

      考虑 Max 加一的效率后,有人采用自制加一,也就是建一个特别的表,字段为:表名,当前序列值。这样在往表中插入值时,先从此表中找到相应表的最大值后加一,进行插入,有人可能发现,也可能会存在并发处理,这个并发处理,我们可以采用 lock 线程的方式来避免,在生成此值的时,先 Lock ,取到值以后,再 unLock 出来,这样不会有两人同时生成了。这比 Max 加一的速度要快多了。

      但同样存在一个问题:在与其他系统集成时,脱离了系统中的生成方法后,很麻烦保证自制表中的最大值与导入后的保持一致,而且数字型都存在上面讲到的“ o ”老数据的导入问题。因此在“自制加一”中可以把主键设为字符型的。字符型的自制加一我倒是蛮推荐的,应该字符型主键可以应付很多我们意想不到的情况。

      1 package com.aspboy.base.database.util;
      2 import java.util.HashMap;
      3 public class KeyGenerator 
      4 {
      5  private static KeyGenerator keygen=new KeyGenerator();
      6  private static final int POOL_SIZE=20;
      7  private HashMap keylist=new HashMap(10);
      8  private KeyGenerator()
      9  {}
     10  
     11  public static KeyGenerator getInstance()
     12  {
     13   return keygen;
     14  }
     15  public synchronized int  getNextKey(String keyName)
     16  {
     17   KeyInfo keyinfo;
     18   if (keylist.containsKey(keyName))
     19   {
     20    keyinfo=(KeyInfo)keylist.get(keyName);
     21    System.out.println("key found");
     22   }
     23   else
     24   {
     25    keyinfo=new KeyInfo(POOL_SIZE,keyName);
     26    keylist.put(keyName, keyinfo);
     27    System.out.println("new key created");
     28   }
     29   return keyinfo.getNextKey();
     30  }
     31  
     32 }
     33  
     34  
     35 package com.aspboy.base.database.util;
     36 import com.aspboy.base.database.DBBean;
     37 public class KeyInfo 
     38 {
     39  private int keyMax;
     40  private int Keymin;
     41  private int nextKey;
     42  private int poolSize;
     43  private String keyName;
     44  //private int times ;
     45  
     46  public KeyInfo(int poolSize,String keyName)
     47  {
     48   this.poolSize=poolSize;
     49   this.keyName=keyName;
     50   loadFromDB();
     51   //times++;
     52   //System.out.println("get keyvalue from db tiems=="+times);
     53   
     54  }
     55  public int getKeyMax() {
     56   return keyMax;
     57  }
     58  public int getKeymin() {
     59   return Keymin;
     60  }
     61  
     62  private void loadFromDB()
     63  {
     64   String sql1="update tb_key set KeyValue=KeyValue+"+poolSize+" where keyName='"+keyName+"'";
     65   DBBean.executeSql(sql1);
     66   String sql2="select KeyValue from  tb_key  where keyName='"+keyName+"'";
     67   int keyFromDB=DBBean.getIntSingle(sql2);
     68   keyMax=keyFromDB;
     69   Keymin=keyFromDB-poolSize+1;
     70   nextKey=Keymin;
     71  }
     72  public int getNextKey() {
     73   
     74   if (nextKey>keyMax)
     75   {
     76    loadFromDB();
     77    System.out.println("get keyvalue from db");
     78    
     79   }
     80   return nextKey++;
     81  }
     82  
     83  
     84 }
     85  
     86  
     87  
     88 调用 方法: KeyGenerator.getInstance().getNextKey(keyName);
     89  
     90  
     91 keyName 为表名称
     92  
     93  
     94 相关数据库结构:
     95  
     96 表tb_key
     97  
     98 字段如下:
     99  
    100  KeyValue:pk大小
    101  
    102 keyName:表名
    103  
    View Code

    第五: 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的利大于弊,笔者推荐可以采用此种方式。

    第六:具有实际意义的主键

      有些表可以使用具有实际意义的主键,但这种表为数不多,因为要保证该字段长久的具有行记录唯一的特点,如若有可能变成该表中的非唯一字段,那它就不适合将其变成主键。

      笔者建议有些表可以使用具有实际意义的主键,例如“用户信息表”的“用户登录名”字段基本都是唯一的,而且几乎不可能变成一个登录名对应两条记录,因此可以使用其作为主键。另外,例如“一号通用户信息表”中,“一号通号码”肯定是唯一的,因此也可作为主键。

    我也不是推荐 GUID 最好,其实在不同的情况,我们都可以采用上面的某一种方式,思考了一些利与弊,也方便大家在进行设计时参考。

      1、采用mysql自增长主键策略 
         :简单,不需要程序特别处理 
         :这种方法对以后如果项目移植到其它数据库上改动会比较大,oracle、    db2采用Sequence,mysql、sqlServer又采用自增长,通用性不好 
      2、使用时间戳+随机数 
        :实现简单,与数据库无关,移植性较好 
         :长度太长,最少也得20位,不仅占空间并且建索引的话性能会比较差点吧 
      3、每次取主键最大值+1做为新的主键 
        :主键长度可控,移植性较好 
        :并发写可能会造成主键冲突,对并发也不太好控制 
      4、单独建一个存放主键的表 
        :实现简单,移植性较好 
        :需要考虑并发问题,整个系统主键生成都依赖该表,性能影响可能较大 

    参考网址:http://baike.baidu.com/link?url=gSuhS7RB9PIvTqehyqJzzBtCX4Z4wrvjby_JemLIHsLlLnJBv7VxFdMgTEzHEsxtvGZ00dp3SuMtG_1hKPEF6xqfHT14Fr15B9oCwHAP3ZTE0Qz37IXPTUTfz7ayQVwMnVpRmylCk9-xWc771qW68q

    2.http://blog.csdn.net/5iasp/article/details/4268081

    3.http://www.iteye.com/topic/1132179

    4.http://blog.csdn.net/likika2012/article/details/9949949

    数据库主键

    编辑
    主键:表中经常有一个列或多列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整性。当创建或更改表时可通过定义 PRIMARY KEY 约束来创建主键。一个表只能有一个 PRIMARY KEY 约束,而且 PRIMARY KEY 约束中的列不能接受空值。由于 PRIMARY KEY 约束确保唯一数据,所以经常用来定义标识列
    作用:
    1)保证实体的完整性;
    2)加快数据库的操作速度
    3) 在表中添加新记录时,ACCESS会自动检查新记录的主键值,不允许该值与其他记录的主键值重复。
    4) ACCESS自动按主键值的顺序显示表中的记录。如果没有定义主键,则按输入记录的顺序显示表中的记录。

    主键的特性

    编辑

    主键的必要性

    在有些数据库中,虽然主键不是必需的,但最好为每个表都设置一个主键,不管是单主键还是复合主键。它存在代表着表结构的完整性,表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,以及本记录的修改与删除。

    主键的无意义性

    在开发过程中,读者可能会看到将一些表使用有意义的字段表示主键,例如“用户登录信息表”将“登录名”(英文名)作为主键,“订单表”中将“订单编号”作为主键,如此设计主键一般都是没什么问题,因为将这些主键基本不具有“意义更改”的可能性。但是,也有一些例外的情况,例如“订单表”需要支持需求“订单可以作废,并重新生成订单,而且订单号要保持原订单号一致”,那将“订单编号”作为主键就满足不了要求了。因此读者在使用具有实际意义的字段作为主键时,需要考虑是否存在这种可能性。

    主键的选择

    编辑
    ①编号作主键
    此方法就是采用实际业务中的唯一字段的“编号”作为主键设计,这在小型的项目中是推荐这样做的,因为这可以使项目比较简单化,但在使用中却可能带来一些麻烦,比如要进行“编号修改”时,可能要涉及到很多相关联的其他表,就像黎叔说的“后果很严重”;还有就是上面提到的“业务要求允许编号重复时”,我们再那么先知,都无法知道业务将会修改成什么?
    ②自动编号主键
    这种方法也是很多朋友在使用的,就是新建一个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 键,可能会带来两方面的消极影响:存储空间增大;索引时间较慢。
  • 相关阅读:
    博客园开通新随笔
    遍历两个数组,并输出数组中的不同内容
    1021-二叉树复制和左右子树互换
    1020-层次遍历二叉树
    1019-计算二叉树的高度和结点数
    1018-深度遍历二叉树
    1017-乘积最大
    1016-求幂
    1015-最大公约数和最小公倍数
    1014-数据的插入与删除
  • 原文地址:https://www.cnblogs.com/haimishasha/p/6220353.html
Copyright © 2020-2023  润新知