精华内容
下载资源
问答
  • 在关系型的数据库中,一条记录有若干个字段,若其中某一个字段能唯一标识一条记录,该个字段就可以成为一个主键 比如 学生表 有下面这些字段:(学号,姓名,性别,班级) 其中每个学生的学号是唯一的,学号就是一个

    主键和外键

    在这里插入图片描述
    主键 (Primary Key) 中的每一笔资料都是表格中的唯一值。换言之,它是用来独一无二地确认一个表格中的每一行资料。主键可以是原本资料内的一个栏位,或是一个人造栏位 (与原本资料没有关系的栏位)。主键可以包含一或多个栏位。当主键包含多个栏位时,称为组合键 (Composite Key)

    在关系型的数据库中,一条记录有若干个字段,若其中某一个字段能唯一标识一条记录,该个字段就可以成为一个主键

    比如
    学生表
    有下面这些字段:(学号,姓名,性别,班级)
    其中每个学生的学号是唯一的,学号就是一个主键

    课程表(课程编号,课程名,学分)
    有下面这些字段:(课程编号,课程名,学分)
    其中课程编号是唯一的,课程编号就是一个主键

    成绩表
    有下面这些字段:(学号,课程号,成绩)
    成绩表中单一一个字段无法唯一标识一条记录,学号和课程号的组合才可以唯一标识一条记录,所以 学号和课程号的字段组合是一个主键

    定义主键和外键的原因是因为:
    主键能够唯一的确定一条记录

    外键是用于与另外一张键的关联,是能确定另外一张表记录的字段的
    比如A表的一个字段是B表的主键,那么它就是A表的外键
    在这里插入图片描述

    展开全文
  • 基于关系数据库设计时候,通常要为每张表指定一个主键,所谓主键就是能够唯一标识某一行记录的属性或属性组,一个表只能有一个主键,但可以 有多个候选索引。因为主键可以唯一标识某一行记录,所以可以确保...

    在基于关系型数据库设计时候,通常要为每张表指定一个主键,所谓主键就是能够唯一标识表中某一行记录的属性或属性组,一个表只能有一个主键,但可以 有多个候选索引。因为主键可以唯一标识某一行记录,所以可以确保执行数据更新、删除、修改时不出现错误。当然,其它字段可以辅助我们在执行这些操作时消除 共享冲突,不是本文讨论的重点,不再赘述。主键除了上述作用外,常常与外键构成参照完整性约束,防止出现数据不一致。所以数据库在设计时,主键起到了很重 要的作用。常见的数据库主键选取方式有: 自动增长式、手动增长式 、UniqueIdentifier、联合式(复合式)、时间序列+随机数式、“COMB(Combine)”类型。
    一、自动增长式
    很多数据库设计者喜欢使用自动增长型字段,因为它使用简单。自动增长式允许我们在向数据库添加数据时,不考虑主键的取值,记录插入后,数据库系统会 自动为其分配一个值,确保绝对不会出现重复。如果使用SQL Server数据库的话,我们还可以在记录插入后使用@@IDENTITY全局变量获取系统分配的主键值。 
    尽管自动增长式字段会省掉我们很多繁琐的工作,但使用它也存在潜在的问题,那就是在数据缓冲模式下,很难预先填写主键与外键的值。假设有主辅两张表:
    Order(OrderID, OrderDate)  订单表
    OrderDetial(OrderID, LineNum, ProductID, Price)  订单明细表
    Order 表中的OrderID是自动增长型的字段。假设现在需要我们录入一张订单,包括在Order表中插入一条记录以及在OrderDetail表中插入若干条 记录。因为Order表中的OrderID是自动增长型的字段,那么我们在记录正式插入到数据库之前无法事先得知它的取值,只有在更新后才能知道数据库为 它分配的是什么值。这会造成以下矛盾发生: 
    首先,为了能在OrderDetail的OrderID字段中添入正确的值,必须先更新 Order表以获取到系统为其分配的OrderID值,然后再用这个OrderID填充OrderDetail表的OrderID列。最后更新 OderDetail表。但是,为了确保数据的一致性,Order与OrderDetail在更新时必须在事务模式下进行的,即要么两张表同时同时更新成 功、要么全部失败,显然它们是相互矛盾的。 
     其次,当我们需要在多个数据库间进行数据的复制时(SQL Server的数据分发、订阅机制允许我们进行库间的数据复制操作),自动增长式字段可能造成数据合并时的主键冲突及表关联关系的丢失。设想一个数据库中 的Order表向另一个库中的Order表复制数据库时,OrderID到底该不该自动增长呢?如果自动增长,其子表OrderDetial的关联关系会 丢失,如果不增长就会和现有数据主键重复,是不是很矛盾呢? 
    再次,自增量的值都是需要在系统中维护一个全局的数据值,每次插入数据时即对此次值进行增量取值。当在产生唯一标识的并发环境中,每次的增量取值都必须为此全局值加锁解锁以保证增量的唯一性。造成并发瓶颈,降低查询性能。
        还有当数据表足够大或频繁的更改和插入操作导致主键类型值超出范围,这种情况一般很少碰到,但也是我们进行数据表设计时必须考虑的一个问题

    在实际开发中中容易出现主键冲突 主键冲突就是主键出现重复值
    比如 t_user t_admin 都有主键 都是id 自增方式
    Insert into t_user (id)
    Select id from t_admin
    两表中都有主键为 7的时候 插入数据不成功 因为主键冲突
    解决办法
    UPDATE t_admin SET id=-id
    WHERE id IS NOT NULL AND id>0

    Commit;

    二、手动增长型字段
    既然自动增长型字段会带来如此的麻烦,我们不妨考虑使用手动增长型的字段,也就是说主键的值需要自己维护,通常情况下需要建立一张单独的表存储当前 主键键值。为了叙述上的方便仍然利用上面的例子进行阐述,新建一张表叫IntKey,包含两个字段,KeyName以及KeyValue。就像一个 HashTable,给一个KeyName,就可以知道目前的KeyValue是什么,然后手工实现键值数据递增。在SQL Server中可以编写这样一个存储过程,让取键值的过程自动进行。代码如下:
    CREATE PROCEDURE [GetKey] 
    @KeyName char(10), 
    @KeyValue int OUTPUT 
    AS 
    UPDATE IntKey SET @KeyValue = KeyValue = KeyValue + 1 WHERE KeyName = @KeyName 
    GO
    这样,通过调用存储过程,我们可以获得最新键值,确保不会出现重复。若将OrderID字段设置为手动增长式字段,我们的程序可以由以下几步来实 现:首先调用存储过程,获得一个OrderID,然后使用这个OrderID填充Order表与OrderDetail表,最后在事务机制下对两表进行更 新。 
    使用手动增长式字段作为主键在进行数据库间数据复制时,可以确保数据合并过程中不会出现键值冲突,只要为不同的数据表分配不同的主键取值段 就行了。但是,使用手动增长型字段会增加网络的负担,必须通过增加一次数据库访问来获取当前主键键值,这会增加网络和数据库的负载,当处于一个低速或断开 的网络环境中时,这种做法会有很大的弊端。同时,手工维护主键还要考虑并发冲突等种种因素,这更会增加系统的复杂程度。
    三、使用UniqueIdentifier
    SQL Server为我们提供了UniqueIdentifier数据类型,并提供了一个生成函数NEWID( ),使用NEWID( )可以生成一个唯一的UniqueIdentifier。UniqueIdentifier在数据库中占用16个字节,出现重复的概率几乎为0,号称全球 唯一标识。我们经常从注册表或WINDOWS程序出现错误需要调试时看到类似 768427bf-9b37-4776-97ca-000365e160d5或{45F0EB02-0727-4F2E-AAB5- E8AEDEE0CEC5} 的东西实际上就是一个UniqueIdentifier,Windows用它来做COM组件以及接口的标识,防止出现重复。在.NET中 UniqueIdentifier称之为GUID(Global Unique Identifier)。在C#中可以使用如下命令生成一个GUID: 
    Guid u = System.Guid.NewGuid(); 
    对 于上面提到的Order与OrderDetail的程序,如果选用UniqueIdentifier作为主键的话,我们完全可以避免上面提到的增加网络 RoundTrip的问题。通过程序直接生成GUID填充主键,不用考虑是否会出现重复。 但是UniqueIdentifier 字段也存在严重的缺陷:首先,它的长度是16字节,是整数的4倍长,会占用大量存储空间。更为严重的是,UniqueIdentifier的生成毫无规律 可言,也就是说是无序的,要想在上面建立索引(绝大多数数据库在主键上都有索引)是一个非常耗时的操作。有人做过实验,当数据表记录比较大的时,在不同的 数据量级别上插入同样的数据量,使用 UniqueIdentifier型数据做主键要比使用Integer型数据慢,且还没有考虑到表关联的情况,出于效率考虑,尽可能避免使用 UniqueIdentifier型数据库作为主键值,但随着现代计算机计算速度越来越快,在中小型项目中使用UniqueIdentifier式主键也 是一个选项。
    四、使用业务字段联合主键
        
    基于DEPHI和 POWERBUILDER等数据库工具开发C/S系统的数据库设计人员,习惯上用有业务意义的字段组合成复合主键做数据表主键。使用业务主键当然有其与生 俱来的好处,一般情况下数据库系统会在默认条件下建立聚簇索引,而且这个聚簇索引基于主键升序排列,当数据量比较小时,我们感觉不到这种差别,当数据量比 较大时,这种基于主键定义的聚簇索引的优势就显现出来,这就使得数据表在每次存取数据时按照索引准确确认数据插入或更新的磁盘物理位置,减少磁头寻址时 间,从而提高数据库性能,而且能够从业务意义上保证数据的完整性,增加程序的可靠性。但是基于业务字段的联合索引,当业务字段选用比较多时会占用比较多的 磁盘空间,而且索引页会占用更多的内存页面,从而导致查询命中率降低;另外使用业务主键,当涉及到主键数据的修改时,要在编程过程中记录新值和原值的关系 表,在更新时又要进行新值和原值的比对,增加编写程序的复杂度。
    五、时间序列+随机数主键
    采用精确到毫秒甚至钠秒级的时间和一个随机产生的两位数做主键,如200911282311528+两位随机数,不失为解决主键问题的一个有效办 法。这样产生的主键既避免了UniqueIdentifier型字段做主键时的无序,又能有效避免自动增长型主键带来的诸如复制和数据导入的麻烦。但在使 用用户众多的网络实时系统中,在时间和空间上仍然不能保证唯一性的问题。
    六、使用“COMB(Combine)”类型
    既然上面五种主键类型选取策略都存在各自的缺点,那么到底有没有好的办法加以解决呢?答案是肯定的。通过使用COMB类型(数据库中没有COMB类 型,它是Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中设计出来的),可以在以上众多的主键策略之间采用中庸之道,找到一个很好的平衡点。
    COMB数据类型的基本设计思路是这样的:既然UniqueIdentifier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们 能不能通过组合的方式,保留UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间 信息与 UniqueIdentifier组合起来,在保留UniqueIdentifier的唯一性的同时增加了有序性,以此来提高索引效率。也许有人会担心 UniqueIdentifier减少到10字节会造成数据出现重复,其实不用担心,后6字节的时间精度可以达到1/300秒,两个COMB类型数据完全 相同的可能性是在这1/300秒内生成的两个GUID前10个字节完全相同,这几乎是不可能的!在SQL Server中用SQL命令将这一思路实现出来便是:
    DECLARE @aGuid UNIQUEIDENTIFIER 
    SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) 

    • CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)
      经过测试,使用COMB做主键比使用INT做主键,在检索、插入、更新、删除等操作上仍然显慢,但比Unidentifier类型要快上一些。除了使用存储过程实现COMB数据外,我们也可以使用C#生成COMB数据,这样所有主键生成工作可以在客户端完成。
      C#代码如下: 
      复制代码代码如下:

    //================================================ 
    /<summary> 
    /// 返回 GUID 用于数据库操作,特定的时间代码可以提高检索效率 
    /// </summary> 
    /// <returns>COMB (GUID 与时间混合型) 类型 GUID 数据</returns> 
    public static Guid NewComb() 

    byte[] guidArray = System.Guid.NewGuid().ToByteArray(); 
    DateTime baseDate = new DateTime(1900,1,1); 
    DateTime now = DateTime.Now; 
    // Get the days and milliseconds which will be used to build the byte string 
    TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks); 
    TimeSpan msecs = new TimeSpan(now.Ticks - (new DateTime(now.Year, now.Month, now.Day).Ticks));
    // Convert to a byte array 
    // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
    byte[] daysArray = BitConverter.GetBytes(days.Days); 
    byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds/3.333333)); 
    // Reverse the bytes to match SQL Servers ordering 
    Array.Reverse(daysArray); 
    Array.Reverse(msecsArray); 
    // Copy the bytes into the guid 
    Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); 
    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); 
    return new System.Guid(guidArray); 

    //================================================ 
    /
    <summary> 
    /// 从 SQL SERVER 返回的 GUID 中生成时间信息 
    /// </summary> 
    /// <param name="guid">包含时间信息的 COMB </param> 
    /// <returns>时间</returns> 
    public static DateTime GetDateFromComb(System.Guid guid) 

    DateTime baseDate = new DateTime(1900,1,1); 
    byte[] daysArray = new byte[4]; 
    byte[] msecsArray = new byte[4]; 
    byte[] guidArray = guid.ToByteArray(); 
    // Copy the date parts of the guid to the respective byte arrays. 
    Array.Copy(guidArray, guidArray.Length - 6, daysArray, 2, 2); 
    Array.Copy(guidArray, guidArray.Length - 4, msecsArray, 0, 4); 
    // Reverse the arrays to put them into the appropriate order 
    Array.Reverse(daysArray); 
    Array.Reverse(msecsArray); 
    // Convert the bytes to ints 
    int days = BitConverter.ToInt32(daysArray, 0); 
    int msecs = BitConverter.ToInt32(msecsArray, 0); 
    DateTime date = baseDate.AddDays(days); 
    date = date.AddMilliseconds(msecs * 3.333333); 
    return date; 

    综上述六种主键选取策略,我认为使用“COMB(Combine)”类型做主键是比较恰当的主键应用策略,但在实际使用过程中要根据客观实践、因时因事选取适当的主键,切不可生搬硬套、弄巧成拙。

    转载于:https://blog.51cto.com/10975663/2064917

    展开全文
  • 主键唯一地标识的一行,因此它受到以下约束的约束: 独特 非空 不可变的 选择主键时,我们必须考虑以下方面: 主键可用于通过外键关系连接其他表 主键通常具有关联的默认索引,因此数据类型越...

    数据库主键外键联合主键

    主键类型

    所有数据库表必须具有一个主键列。 主键唯一地标识表中的一行,因此它受到以下约束的约束:

    • 独特
    • 非空
    • 不可变的

    选择主键时,我们必须考虑以下方面:

    • 主键可用于通过外键关系连接其他表
    • 主键通常具有关联的默认索引,因此数据类型越紧凑,索引将占用的空间越少
    • 一个简单的键比复合键的性能更好
    • 即使在高度并发的环境中,主键分配也必须确保唯一性

    选择主键生成器策略时,选项为:

    1. 自然键,使用保证单个行唯一性的列组合
    2. 替代键,独立于当前行数据生成

    自然键

    自然密钥的唯一性受外部因素(例如,人的唯一标识符,社会保险号,车辆识别号)的影响。

    自然键很方便,因为它们具有与外界等效的功能,并且不需要任何额外的数据库处理。 因此,即使在将实际行插入数据库之前,我们也可以知道主键,从而简化了批处理插入。

    如果自然键是单个数字值,则性能可与替代键的性能相媲美。

    对于复合键,我们必须意识到可能的性能损失:

    • 复合键联接比单键联接慢
    • 复合键索引比单键索引需要更多空间

    对于索引和连接,非数字键的效率低于数字键(整数,bigint)。 CHAR(17)自然密钥(例如,车辆识别号)占用17个字节,而不是4个字节(32位整数)或8个字节(64位bigint)。

    最初的模式设计唯一性假设可能不会永远成立。 假设我们使用了一个特定的国家/地区公民数字代码来标识所有应用程序用户。 如果现在我们需要支持其他没有此类公民数字代码或与现有条目冲突的国家,那么我们可以得出结论,架构的发展可能受到阻碍。

    如果自然键唯一性约束发生变化,将很难同时更新主键(如果我们仍然设法删除了主键约束)和所有关联的外键关系。

    代理键

    代理键是独立于当前行数据而生成的,因此其他列约束可以根据应用程序业务需求自由发展。

    数据库系统可以管理代理密钥的生成,并且密钥通常是数字类型(例如,整数或bigint),每当需要新密钥时就递增。

    如果我们想控制代理密钥的生成,我们可以使用128位GUIDUUID 由于不再需要额外的数据库密钥生成处理,因此简化了批处理并可以提高插入性能。 即使未广泛采用此策略,在设计数据库模型时也值得考虑

    当数据库标识符生成责任落在数据库系统上时,有几种策略可以自动增加代理键:

    数据库引擎 自动递增策略
    Oracle 序列
    微软SQL 身份 顺序
    PostgreSQL 序列串行类型
    MySQL 自动递增
    DB2 身份 顺序
    数据库 身份 顺序

    设计方面

    翻译自: https://www.javacodegeeks.com/2014/06/database-primary-key-flavors.html

    数据库主键外键联合主键

    展开全文
  • 数据库主键在数据库中占有重要地位。主键的选取策略决定了系统是否可靠、易用、高效。本文探讨了数据库设计过程当中常见的主键选取策略,并剖析了其做主键的优缺点,提出了相应的解决问题的方法 基于关系型数据库...

    转自:

    http://www.jb51.net/article/40933.htm

    数据库主键在数据库中占有重要地位。主键的选取策略决定了系统是否可靠、易用、高效。本文探讨了数据库设计过程当中常见的主键选取策略,并剖析了其做主键的优缺点,提出了相应的解决问题的方法

    在基于关系型数据库设计时候,通常要为每张表指定一个主键,所谓主键就是能够唯一标识表中某一行记录的属性或属性组,一个表只能有一个主键,但可以有多个候选索引。因为主键可以唯一标识某一行记录,所以可以确保执行数据更新、删除、修改时不出现错误。当然,其它字段可以辅助我们在执行这些操作时消除共享冲突,不是本文讨论的重点,不再赘述。主键除了上述作用外,常常与外键构成参照完整性约束,防止出现数据不一致。所以数据库在设计时,主键起到了很重要的作用。常见的数据库主键选取方式有: 自动增长式、手动增长式 、UniqueIdentifier、联合式(复合式)、时间序列+随机数式、“COMB(Combine)”类型。

    一、自动增长式

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

    Order(OrderID, OrderDate)  订单表
    OrderDetial(OrderID, LineNum, ProductID, Price)  订单明细表

    Order 表中的OrderID是自动增长型的字段。假设现在需要我们录入一张订单,包括在Order表中插入一条记录以及在OrderDetail表中插入若干条记录。因为Order表中的OrderID是自动增长型的字段,那么我们在记录正式插入到数据库之前无法事先得知它的取值,只有在更新后才能知道数据库为它分配的是什么值。这会造成以下矛盾发生: 
    首先,为了能在OrderDetail的OrderID字段中添入正确的值,必须先更新 Order表以获取到系统为其分配的OrderID值,然后再用这个OrderID填充OrderDetail表的OrderID列。最后更新OderDetail表。但是,为了确保数据的一致性,Order与OrderDetail在更新时必须在事务模式下进行的,即要么两张表同时同时更新成功、要么全部失败,显然它们是相互矛盾的。 
     其次,当我们需要在多个数据库间进行数据的复制时(SQL Server的数据分发、订阅机制允许我们进行库间的数据复制操作),自动增长式字段可能造成数据合并时的主键冲突及表关联关系的丢失。设想一个数据库中的Order表向另一个库中的Order表复制数据库时,OrderID到底该不该自动增长呢?如果自动增长,其子表OrderDetial的关联关系会丢失,如果不增长就会和现有数据主键重复,是不是很矛盾呢? 
    再次,自增量的值都是需要在系统中维护一个全局的数据值,每次插入数据时即对此次值进行增量取值。当在产生唯一标识的并发环境中,每次的增量取值都必须为此全局值加锁解锁以保证增量的唯一性。造成并发瓶颈,降低查询性能。
        还有当数据表足够大或频繁的更改和插入操作导致主键类型值超出范围,这种情况一般很少碰到,但也是我们进行数据表设计时必须考虑的一个问题

    二、手动增长型字段

    既然自动增长型字段会带来如此的麻烦,我们不妨考虑使用手动增长型的字段,也就是说主键的值需要自己维护,通常情况下需要建立一张单独的表存储当前主键键值。为了叙述上的方便仍然利用上面的例子进行阐述,新建一张表叫IntKey,包含两个字段,KeyName以及KeyValue。就像一个HashTable,给一个KeyName,就可以知道目前的KeyValue是什么,然后手工实现键值数据递增。在SQL Server中可以编写这样一个存储过程,让取键值的过程自动进行。代码如下:

    CREATE PROCEDURE [GetKey] 
    @KeyName char(10), 
    @KeyValue int OUTPUT 
    AS 
    UPDATE IntKey SET @KeyValue = KeyValue = KeyValue + 1 WHERE KeyName = @KeyName 
    GO

    这样,通过调用存储过程,我们可以获得最新键值,确保不会出现重复。若将OrderID字段设置为手动增长式字段,我们的程序可以由以下几步来实现:首先调用存储过程,获得一个OrderID,然后使用这个OrderID填充Order表与OrderDetail表,最后在事务机制下对两表进行更新。 
    使用手动增长式字段作为主键在进行数据库间数据复制时,可以确保数据合并过程中不会出现键值冲突,只要为不同的数据表分配不同的主键取值段就行了。但是,使用手动增长型字段会增加网络的负担,必须通过增加一次数据库访问来获取当前主键键值,这会增加网络和数据库的负载,当处于一个低速或断开的网络环境中时,这种做法会有很大的弊端。同时,手工维护主键还要考虑并发冲突等种种因素,这更会增加系统的复杂程度。

    三、使用UniqueIdentifier

    SQL Server为我们提供了UniqueIdentifier数据类型,并提供了一个生成函数NEWID( ),使用NEWID( )可以生成一个唯一的UniqueIdentifier。UniqueIdentifier在数据库中占用16个字节,出现重复的概率几乎为0,号称全球唯一标识。我们经常从注册表或WINDOWS程序出现错误需要调试时看到类似 768427bf-9b37-4776-97ca-000365e160d5或{45F0EB02-0727-4F2E-AAB5-E8AEDEE0CEC5} 的东西实际上就是一个UniqueIdentifier,Windows用它来做COM组件以及接口的标识,防止出现重复。在.NET中 UniqueIdentifier称之为GUID(Global Unique Identifier)。在C#中可以使用如下命令生成一个GUID: 
    Guid u = System.Guid.NewGuid(); 
    对于上面提到的Order与OrderDetail的程序,如果选用UniqueIdentifier作为主键的话,我们完全可以避免上面提到的增加网络RoundTrip的问题。通过程序直接生成GUID填充主键,不用考虑是否会出现重复。 但是UniqueIdentifier 字段也存在严重的缺陷:首先,它的长度是16字节,是整数的4倍长,会占用大量存储空间。更为严重的是,UniqueIdentifier的生成毫无规律可言,也就是说是无序的,要想在上面建立索引(绝大多数数据库在主键上都有索引)是一个非常耗时的操作。有人做过实验,当数据表记录比较大的时,在不同的数据量级别上插入同样的数据量,使用 UniqueIdentifier型数据做主键要比使用Integer型数据慢,且还没有考虑到表关联的情况,出于效率考虑,尽可能避免使用UniqueIdentifier型数据库作为主键值,但随着现代计算机计算速度越来越快,在中小型项目中使用UniqueIdentifier式主键也是一个选项。

    四、使用业务字段联合主键
        
    基于DEPHI和POWERBUILDER等数据库工具开发C/S系统的数据库设计人员,习惯上用有业务意义的字段组合成复合主键做数据表主键。使用业务主键当然有其与生俱来的好处,一般情况下数据库系统会在默认条件下建立聚簇索引,而且这个聚簇索引基于主键升序排列,当数据量比较小时,我们感觉不到这种差别,当数据量比较大时,这种基于主键定义的聚簇索引的优势就显现出来,这就使得数据表在每次存取数据时按照索引准确确认数据插入或更新的磁盘物理位置,减少磁头寻址时间,从而提高数据库性能,而且能够从业务意义上保证数据的完整性,增加程序的可靠性。但是基于业务字段的联合索引,当业务字段选用比较多时会占用比较多的磁盘空间,而且索引页会占用更多的内存页面,从而导致查询命中率降低;另外使用业务主键,当涉及到主键数据的修改时,要在编程过程中记录新值和原值的关系表,在更新时又要进行新值和原值的比对,增加编写程序的复杂度。

    五、时间序列+随机数主键

    采用精确到毫秒甚至钠秒级的时间和一个随机产生的两位数做主键,如200911282311528+两位随机数,不失为解决主键问题的一个有效办法。这样产生的主键既避免了UniqueIdentifier型字段做主键时的无序,又能有效避免自动增长型主键带来的诸如复制和数据导入的麻烦。但在使用用户众多的网络实时系统中,在时间和空间上仍然不能保证唯一性的问题。

    六、使用“COMB(Combine)”类型

    既然上面五种主键类型选取策略都存在各自的缺点,那么到底有没有好的办法加以解决呢?答案是肯定的。通过使用COMB类型(数据库中没有COMB类型,它是Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中设计出来的),可以在以上众多的主键策略之间采用中庸之道,找到一个很好的平衡点。

    COMB数据类型的基本设计思路是这样的:既然UniqueIdentifier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们能不能通过组合的方式,保留UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与 UniqueIdentifier组合起来,在保留UniqueIdentifier的唯一性的同时增加了有序性,以此来提高索引效率。也许有人会担心 UniqueIdentifier减少到10字节会造成数据出现重复,其实不用担心,后6字节的时间精度可以达到1/300秒,两个COMB类型数据完全相同的可能性是在这1/300秒内生成的两个GUID前10个字节完全相同,这几乎是不可能的!在SQL Server中用SQL命令将这一思路实现出来便是:

    DECLARE @aGuid UNIQUEIDENTIFIER 
    SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) 
    + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)

    经过测试,使用COMB做主键比使用INT做主键,在检索、插入、更新、删除等操作上仍然显慢,但比Unidentifier类型要快上一些。除了使用存储过程实现COMB数据外,我们也可以使用C#生成COMB数据,这样所有主键生成工作可以在客户端完成。

    C#代码如下: 

    复制代码代码如下:

    //================================================ 
    /**<summary> 
    /// 返回 GUID 用于数据库操作,特定的时间代码可以提高检索效率 
    /// </summary> 
    /// <returns>COMB (GUID 与时间混合型) 类型 GUID 数据</returns> 
    public static Guid NewComb() 

    byte[] guidArray = System.Guid.NewGuid().ToByteArray(); 
    DateTime baseDate = new DateTime(1900,1,1); 
    DateTime now = DateTime.Now; 
    // Get the days and milliseconds which will be used to build the byte string 
    TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks); 
    TimeSpan msecs = new TimeSpan(now.Ticks - (new DateTime(now.Year, now.Month, now.Day).Ticks));
    // Convert to a byte array 
    // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
    byte[] daysArray = BitConverter.GetBytes(days.Days); 
    byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds/3.333333)); 
    // Reverse the bytes to match SQL Servers ordering 
    Array.Reverse(daysArray); 
    Array.Reverse(msecsArray); 
    // Copy the bytes into the guid 
    Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); 
    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); 
    return new System.Guid(guidArray); 

    //================================================ 
    /** <summary> 
    /// 从 SQL SERVER 返回的 GUID 中生成时间信息 
    /// </summary> 
    /// <param name="guid">包含时间信息的 COMB </param> 
    /// <returns>时间</returns> 
    public static DateTime GetDateFromComb(System.Guid guid) 

    DateTime baseDate = new DateTime(1900,1,1); 
    byte[] daysArray = new byte[4]; 
    byte[] msecsArray = new byte[4]; 
    byte[] guidArray = guid.ToByteArray(); 
    // Copy the date parts of the guid to the respective byte arrays. 
    Array.Copy(guidArray, guidArray.Length - 6, daysArray, 2, 2); 
    Array.Copy(guidArray, guidArray.Length - 4, msecsArray, 0, 4); 
    // Reverse the arrays to put them into the appropriate order 
    Array.Reverse(daysArray); 
    Array.Reverse(msecsArray); 
    // Convert the bytes to ints 
    int days = BitConverter.ToInt32(daysArray, 0); 
    int msecs = BitConverter.ToInt32(msecsArray, 0); 
    DateTime date = baseDate.AddDays(days); 
    date = date.AddMilliseconds(msecs * 3.333333); 
    return date; 

     

    综上述六种主键选取策略,笔者认为使用“COMB(Combine)”类型做主键是比较恰当的主键应用策略,但在实际使用过程中要根据客观实践、因时因事选取适当的主键,切不可生搬硬套、弄巧成拙。

    参考文献:

    1、《系统分析师教程》 张友生 主编
    2、《中文版SQL Server 2000开发与管理应用实例》 邹建 主编
    3、《数据库中使用自增量字段与Guid字段主键的性能对比》作者 不详
    4、《小议数据库主键选取策略》 作者 不详

    转载于:https://www.cnblogs.com/tylertang/p/5982269.html

    展开全文
  • 数据库主键与外键

    2019-01-09 00:44:38
    数据库主键与外键 ...如果用户看到了一个表示多对多关系的连接表的数据,并抱怨它没有什么用处,那就证明它的主键设计地很好。 主键应该是单列的,以便提高连接和筛选操作的效率。 主键的作用 (1)索引:...
  • 展开全部主码:我们建立数据库32313133353236313431303231363533e58685e5aeb931333433626439的时候,需要为每张表指定一个主码,主码也叫主键.比如,你有一个员工的二维关系(表),大概这几个属性:员工表:系统内...
  • 但是一直不太懂 其实算是前辈们经验之谈了吧 ,因为实际应用中 ,数据库中的每条记录都可能变更,今日改明日删,而删除,又并非实际的删除,而是加一个标识字段(数据关系完整性考虑)。这样您认为的能够...
  • 数据库主键风格

    2020-04-20 17:18:05
    主键唯一地标识的一行,因此它受到以下约束的约束: 独特 非空 不可变的 选择主键时,我们必须考虑以下方面: 主键可用于通过外键关系连接其他表 主键通常具有关联的默认索引,因此数据类型越...
  • 1.主键关系数据库中,一条记录中有若干属性,若某一个属性组能唯一标识一条记录,这个属性组就是主键。学生表(学号,姓名,性别,班级) 每个学生的学号是唯一的,学号是主键 成绩表(学号,课程号,成绩) 一个...
  • 在数据库什么是主键与外键2008-03-05 15:03这需要理清几个概念: 1)候选键: 关系中的一个属性组,其值能唯一标识一个元组,若从该属性组去掉任何一个属性,它就不具有这一性质了,这样的属性组称作候选码。...
  • 数据库主键的设计

    2016-07-18 09:41:22
    在数据库设计时,主要就是对实体和关系的设计,实体表现出来就是表,关系表现出来就是外键。而对于一个表,由两部分组成:主键和属性。主键的简单定义就是表中为每一行数据的唯一标识。其实更准确的说法,每一行数据...
  • 主关键字(PRIMARY KEY):主键是表的一个或多个字段,它的值用于唯一地标识的某一条记录。 外键(FOREIGN KEY):如果公共关键字一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见...
  • * 第4章 数据库和表的管理表和表约束的创建 第8讲 SQL Server 2008 * * * * * * * * 动手操作1创建kc表和表约束 要求用命令方式创建数据KC表单列后直接定义约束 表4-3 课程表KC的结构描述 列名 数据类型 长度 属性...
  • 一直以来不能够分清主键和索引的关系此梳理以备不时之需 1、主键  主键就是能够唯一标识某一行的属性或属性组,一个表只能有一个主键,但可以有多个候选索引。  主键主要作用:1、惟一地标识一行。 2、...
  • 关系数据库

    2019-06-19 09:09:01
    候选键:一个关系中,某一属性(或属性集)可唯一地标识每一个元组。 主键:选用一个候选键作为组织关系及唯一性操作的对象。 外键:若关系R1的属性(或属性集)A1不是R1的候选键,而是另一关系的候选键,则称A1为...
  • 一个关系表里面,应该有各种约束来维持表的关系一个表存在常见的约束: 主键 :primary key 外键:foreign key ... 一张表,用来唯一标识一条记录的字段集,叫做主关键字或者主关键码...
  • 数据库 主键与索引键的区别

    千次阅读 2013-05-29 14:03:40
    关系数据库依赖于主键,它是数据库物理模式的基石。主键在物理层面上只有两个用途: 惟一地标识一行。 作为一个可以被外键有效引用的对象。 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分...
  • 一个关系中,能唯一标识元组的属性或属性集,称为关系的超键。 候选键(candidatekey): 不含有多余属性的超键,称为候选键。 主键(primarykey): 用户选作元组的唯一标识的一个候选键,称为主键。 用主键...
  • 主键是一个关系的唯一标识,比如学生关系表(学号,姓名,系别),将‘学号’定义为主键,因为一个学号只能对应一个学生...而在数据库中如果要查询一个学生所在系的系主任的名字,就通过外键‘系别’将两个表之间建立关
  • 元组:二维表中的一行,在数据库中被称为记录 属性:二维表中的一列,在数据库中被称为字段 域:属性的取值范围,也就是数据库中某一列的取值限制 关键字:一组可以唯一标识元组的属性,数据库中常称为主键,由一个...
  • 元组:二维表中的一行,在数据库中被称为记录 属性:二维表中的一列,在数据库中被称为字段 域:属性的取值范围,也就是数据库中某一列的取值限制 关键字:一组可以唯一标识元组的属性,数据库中常称为主键,由一个...
  • 在数据库设计时,主要就是对实体和关系的设计,实体表现出来就是表,关系表现出来就是外键。而对于一个表,由两部分组成:主键和属性。主键的简单定义就是表中为每一行数据的唯一标识。其实更准确的说法,每一行数据...
  • 在关系数据库设计,业务主键是一个由以及真实存在于世界的属性构成的键。业务主键对于逻辑主键的主要优势在于(逻辑主键在脱离数据库环境时没有任何意义)业务主键已经存在,因此没有必要去添加新的人...
  • 关系数据库mysql

    2019-10-30 09:49:33
    关系型数据库 关系型数据库源于关系模型 关系模型认为,世界是由实体和实体之间的联系组成 关系型数据库是一种以表作为实体,以...在关系数据库中,外键(Forergn Key)是用来表达表和表之间关联的列 一对一关系 ...
  •  在数据库设计时,主要就是对实体和关系的设计,实体表现出来就是表,关系表现出来就是外键。而对于一个表,由两部分组成:主键和属性。主键的简单定义就是表中为每一行数据的唯一标识。其实更准确的说法,每一行...
  • 关系数据库

    2016-09-26 12:32:14
    关系型模型:把世界看做是由实体和联系组成的。所谓实体就是指在现实世界客观存在并可相互区别的事物。...主键在关系数据库,用一个唯一的标识符来标识每一行,这个标识符就是主键
  • 主键在关系中能够唯一标识的不同行的属性或属性组合,并且这些属性值不包括空值和重复值,用Primary Key表示。 外键:某个表的主键常被引用为另一个表的外键。eg:学号是学生信息表的主键,而不是学生成绩表的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 780
精华内容 312
关键字:

在关系数据库中主键标识