iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >数据库设计中如何实现主键
  • 873
分享到

数据库设计中如何实现主键

2024-04-02 19:04:59 873人浏览 泡泡鱼
摘要

本篇内容主要讲解“数据库设计中如何实现主键”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“数据库设计中如何实现主键”吧!在基于关系型数据库设计时候,通常要为每张表

本篇内容主要讲解“数据库设计中如何实现主键”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习数据库设计中如何实现主键”吧!

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

到此,相信大家对“数据库设计中如何实现主键”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

您可能感兴趣的文档:

--结束END--

本文标题: 数据库设计中如何实现主键

本文链接: https://www.lsjlt.com/news/61132.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 数据库设计中如何实现主键
    本篇内容主要讲解“数据库设计中如何实现主键”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“数据库设计中如何实现主键”吧!在基于关系型数据库设计时候,通常要为每张表...
    99+
    2024-04-02
  • 如何实现数据库中主从表的设计
    本篇内容主要讲解“如何实现数据库中主从表的设计”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何实现数据库中主从表的设计”吧!举一个例子,我现在有一些新闻信息,...
    99+
    2024-04-02
  • 数据库设计中的关键角色:主键与外键
    ...
    99+
    2024-04-02
  • 数据库双主键如何设置
    在数据库中,双主键是指表中的两个字段组合起来作为主键,保证了表中的每行数据唯一性。在设置双主键时,需要使用CREATE TABLE语...
    99+
    2024-02-29
    数据库
  • 主键与外键:数据库设计中的必备工具
    ...
    99+
    2024-04-02
  • 理解主键和外键:数据库设计的基石
    ...
    99+
    2024-04-02
  • oracle数据库如何设置主键自增
    这篇文章将为大家详细讲解有关oracle数据库如何设置主键自增,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Oracle 数据库设置主键自增 设置主键自增序列 创建一个序列: CREATE SEQUE...
    99+
    2024-05-14
    序列 IDENTITY 触发器 主键自增 性能
  • 数据库中怎么设置主键
    这篇文章主要介绍“数据库中怎么设置主键”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“数据库中怎么设置主键”文章能帮助大家解决问题。主键设置创建表的时候我们一般都会设置一个主键(PRIMARY KEY...
    99+
    2023-06-08
  • 如何实现良好的数据库设计
    本篇内容介绍了“如何实现良好的数据库设计”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 为什么要关注数...
    99+
    2024-04-02
  • MySQL 复合主键在数据库设计中的应用详解
    MySQL 复合主键在数据库设计中的应用详解 MySQL 复合主键是指由多个字段组成的主键,通过组合这些字段的值来唯一标识一条记录。在数据库设计中,复合主键的应用非常广泛,特别是在需要...
    99+
    2024-03-15
    mysql 数据库设计 复合主键
  • 如何在SQL数据库中使用主键
    这篇文章将为大家详细讲解有关如何在SQL数据库中使用主键,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。SQL数据库中主键(Primary Key) 中的每一...
    99+
    2024-04-02
  • navicat数据库主键怎么设置
    在 navicat 中设置数据库主键的步骤如下:选择要设置主键的表。打开“设计表”选项卡。选择要设置为主键的列。启用主键约束。可选:设置主键属性(自增幅度、起始值、是否允许空值)。保存更...
    99+
    2024-04-24
    navicat
  • 数据库设置主键的方法
    这篇文章主要介绍数据库设置主键的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!数据库如何设置主键?SQL 设置主键的方法打开【SQL Server Management Stud...
    99+
    2024-04-02
  • 数据库现代化的关键:数据库设计工具的崛起
    数据库设计工具的引入彻底革新了数据库现代化的进程,为设计、开发和管理复杂数据库系统提供了强大的手段。 数据库设计工具的优势 1. 视觉化建模 数据库设计工具允许用户以直观的方式创建实体关系图 (ERD) 和数据流图 (DFD),从而简化数...
    99+
    2024-03-13
    数据库设计工具
  • 数据库设计的主要问题
    数据库设计的主要问题有以下几个:1. 数据冗余:冗余数据是指在数据库中存在多个副本或重复的数据。这会导致数据的一致性问题,增加了存储...
    99+
    2023-10-12
    数据库
  • wamp的数据库如何取消主键
    要取消Wamp中的数据库表的主键,可以执行以下步骤:1. 打开Wamp服务器,并确保MySQL服务已启动。2. 在浏览器中输入 `h...
    99+
    2023-08-11
    wamp 数据库
  • 如何设计数据库索引
    本篇内容主要讲解“如何设计数据库索引”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何设计数据库索引”吧!MySQL中的索引MySQL中的InnoDB引擎使用B...
    99+
    2024-04-02
  • sql数据库设置主键的方法
    这篇文章主要介绍了sql数据库设置主键的方法,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。设置方法:1、使用SQL Server Managem...
    99+
    2024-04-02
  • sql数据库如何设置外键
    在 SQL 数据库中,可以使用以下步骤来设置外键:1. 创建主表和从表。主表包含要作为主键的列,从表包含要作为外键的列。```CRE...
    99+
    2023-10-12
    sql数据库
  • oracle数据库表怎么实现自增主键
    这篇文章主要讲解了“oracle数据库表怎么实现自增主键”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“oracle数据库表怎么实现自增主键”吧!一、前言几天建表需要用到自增主键,于是使用序列...
    99+
    2023-07-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作