SQL数据库中的主键与外键的介绍

一、什么是主键、外键:

关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键比如 :

学生表(学号,姓名,性别,班级)

其中每个学生的学号是唯一的,学号就是一个主键

用户表(用户名、密码、登录级别)

其中用户名是唯一的, 用户名就是一个主键

上机记录表(卡号,学号,姓名、序列号)

上机记录表中单一一个属性无法唯一标识一条记录,学号和姓名的组合才可以唯一标识一条记录,所以 学号和姓名的属性组是一个主键

上机记录表中的序列号不是成绩表的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键

定义主键和外键主要是为了维护关系数据库的完整性,总结一下:

主键是能确定一条记录的唯一标识,比如,一条记录包括身份证号,姓名,年龄。身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号是主键。

外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。比如,A表中的一个字段,是B表的主键,那他就可以是A表的外键。

二、 主键、外键 和索引的区别

主键、外键和索引的区别?

定义: 唯一标识一条记录,不能有重复的,不允许为空 表的外键是另一表的主键, 外键可以有重复的, 可以是空值
该字段没有重复值,但可以有一个空值作用: 用来保证数据完整性 用来和其他表建立联系用的 是提高查询排序的速度个数: 主键只能有一个
一个表可以有多个外键 一个表可以有多个惟一索引

聚集索引和非聚集索引的区别?聚集索引一定是唯一索引。但唯一索引不一定是聚集索引。

聚集索引,在索引页里直接存放数据,而非聚集索引在索引页里存放的是索引,这些索引指向专门的数据页的数据。

三、数据库中主键和外键的设计原则


键和外键是把多个表组织为一个有效的关系数据库的粘合剂。主键和外键的设计对物理数据库的性能和可用性都有着决定性的影响。必须将数据库模式从理论上的逻
辑设计转换为实际的物理设计。而主键和外键的结构是这个设计过程的症结所在。一旦将所设计的数据库用于了生产环境,就很难对这些键进行修改,所以在开发阶
段就设计好主键和外键就是非常必要和值得的。

主键:

关系数据库依赖于主键---它是数据库物理模式的基石。主键在物理层面上只有两个用途:

1. 惟一地标识一行。

2. 作为一个可以被外键有效引用的对象。

基于以上这两个用途,下面给出了我在设计物理层面的主键时所遵循的一些原则:

1. 主键应当是对用户没有意义的。如果用户看到了一个表示多对多关系的连接表中的数据,并抱怨它没有什么用处,那就证明它的主键设计地很好。

2. 主键应该是单列的,以便提高连接和筛选操作的效率。

注:
使用复合键的人通常有两个理由为自己开脱,而这两个理由都是错误的。其一是主键应当具有实际意义,然而,让主键具有意义只不过是给人为地破坏数据库提供了
方便。其二是利用这种方法可以在描述多对多关系的连接表中使用两个外部键来作为主键,我也反对这种做法,理由是:复合主键常常导致不良的外键,即当连接表
成为另一个从表的主表,而依据上面的第二种方法成为这个表主键的一部分,然而这个表又有可能再成为其它从表的主表,其主键又有可能成了其它从表主键的一部
分,如此传递下去,越靠后的从表,其主键将会包含越多的列了。

3.
永远也不要更新主键。实际上,因为主键除了惟一地标识一行之外,再没有其他的用途了,所以也就没有理由去对它更新。如果主键需要更新,则说明主键应对用户无意义的原则被违反了。

注:这项原则对于那些经常需要在数据转换或多数据库合并时进行数据整理的数据并不适用。

4. 主键不应包含动态变化的数据,如时间戳、创建时间列、修改时间列等。

5.
主键应当有计算机自动生成。如果由人来对主键的创建进行干预,就会使它带有除了惟一标识一行以外的意义。一旦越过这个界限,就可能产生认为修改主键的动机,这样,这种系统用来链接记录行、管理记录行的关键手段就会落入不了解数据库设计的人的手中。

四、数据库主键选取策略


们在建立数据库的时候,需要为每张表指定一个主键,所谓主键就是能够唯一标识表中某一行的属性或属性组,一个表只能有一个主键,但可以有多个候选索引。因
为主键可以唯一标识某一行记录,所以可以确保执行数据更新、删除的时候不会出现张冠李戴的错误。当然,其它字段可以辅助我们在执行这些操作时消除共享冲
突,不过就不在这里讨论了。主键除了上述作用外,常常与外键构成参照完整性约束,防止出现数据不一致。所以数据库在设计时,主键起到了很重要的作用。

常见的数据库主键选取方式有:

• 自动增长字段

• 手动增长字段

• UniqueIdentifier

• “COMB(Combine)”类型

1自动增长型字段很多数据库设计者喜欢使用自动增长型字段,因为它使用简单。自动增长型字段允许我们在向数据库添加数据时,不考虑主键的取值,记录插入后,数据库系统会自动为其分配一个值,确保绝对不会出现重复。如果使用SQL
Server数据库的话,我们还可以在记录插入后使用@@Identity全局变量获取系统分配的主键键值。

尽管自动增长型字段会省掉我们很多繁琐的工作,但使用它也存在潜在的问题,那就是在数据缓冲模式下,很难预先填写主键与外键的值。

假设有两张表:

Order(OrderID, OrderDate)

OrderDetial(OrderID, LineNum, ProductID, Price)

Order
表中的OrderID是自动增长型的字段。现在需要我们录入一张订单,包括在Order表中插入一条记录以及在OrderDetail表中插入若干条记
录。因为Order表中的OrderID是

自动增长型的字段,那么我们在记录正式插入到数据库之前无法事先得知它的取值,只有在更新后才能知道数据库为它
分配的是什么值。这会造成以下矛盾发生:

首先,为了能在OrderDetail的OrderID字段中添入正确的值,必须先更新Order
表以获取到系统为其分配的OrderID值,然后再用这个OrderID填充OrderDetail表。最后更新

OderDetail表。但是,为了确保
数据的一致性,Order与OrderDetail在更新时必须在事务保护下同时进行,即确保两表同时更行成功。显然它们是相互矛盾的。

除此之外,当我们需要在多个数据库间进行数据的复制时(SQL

Server的数据分发、订阅机制允许我们进行库间的数据复制操作),自动增长型字段可能造成数据合并时的主键冲突。设想一个数据库中的Order表向另一个库中的Order表复制数

据库时,OrderID到底该不该自动增长呢?

ADO.NET
允许我们在DataSet中将某一个字段设置为自动增长型字段,但千万记住,这个自动增长字段仅仅是个占位符而已,当数据库进行更新时,数据库生成的值会
自动取代

ADO.NET分配的值。所以为了防止用户产生误解,建议大家将ADO.NET中的自动增长初始值以及增量都设置成-1。此外,在ADO.NET
中,我们可以为两张表建立

DataRelation,这样存在级联关系的两张表更新时,一张表更新后另外一张表对应键的值也会自动发生变化,这会大大减少
了我们对存在级联关系的两表间更新时自动增长型字段

带来的麻烦。

2手动增长型字段既然自动增长型字段会带来如此的麻烦,我们不妨考虑使用手
动增长型的字段,也就是说主键的值需要自己维护,通常情况下需要建立一张单独的表存储当前主键

键值。还用上面的例子来说,这次我们新建一张表叫
IntKey,包含两个字段,KeyName以及KeyValue。就像一个HashTable,给一个KeyName,就可以知道目前的
KeyValue

是什么,然后手工实现键值数据递增。在SQL

Server中可以编写这样一个存储过程,让取键值的过程自动进行。代码如下:

CREATE PROCEDURE[GetKey]

@KeyNamechar(10),

@KeyValue intOUTPUT AS UPDATE IntKey SET @KeyValue =KeyValue = KeyValue + 1

WHERE KeyName = @KeyName GO


样,通过调用存储过程,我们可以获得最新键值,确保不会出现重复。若将OrderID字段设置为手动增长型字段,我们的程序可以由以下几步来实现:首先调
用存储过程,获得

一个OrderID,然后使用这个OrderID填充Order表与OrderDetail表,最后在事务保护下对两表进行更新。

使
用手动增长型字段作为主键在进行数据库间数据复制时,可以确保数据合并过程中不会出现键值冲突,只要我们为不同的数据库分配不同的主键取值段就行了。但
是,使用手动

增长型字段会增加网络的RoundTrip,我们必须通过增加一次数据库访问来获取当前主键键值,这会增加网络和数据库的负载,当处于一个低
速或断开的网络环境中时,这种做法

会有很大的弊端。同时,手工维护主键还要考虑并发冲突等种种因素,这更会增加系统的复杂程度。

3使用UniqueIdentifierSQL Server为我们提供了UniqueIdentifier数据类型,并提供了一个生成函数NEWID(
),使用NEWID(
)可以生成一个唯一的UniqueIdentifier。UniqueIdentifier在数据库中占用16个字节,出现重复的概率非常小,以至于可以认为是0。我们经常从注册表中看到类似

{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型
数据库作为主键键值。

4使用“COMB(Combine)”类型既然上面三种主键类型选取策略都存在各自的缺点,那么到底有没有好的办法加以解决呢?答案是肯定的。通过使用COMB类型(数据库中没有

COMB类型,它是Jimmy
Nilsson在他的“The Cost of GUIDs asPrimary Keys”一文中设计出来的),可以在三者之间找到一个很好的平衡点。

COMB
数据类型的基本设计思路是这样的:既然UniqueIdentifier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们能不能通过组合
的方式,保留

UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与
UniqueIdentifier组合起来,在保留UniqueIdentifier的唯一性的同时

增加了有序性,以此来提高索引效率。也许有人会担心
UniqueIdentifier减少到10字节会造成数据出现重复,其实不用担心,后6字节的时间精度可以达到1/300秒,两个COMB类

型数据完全
相同的可能性是在这1/300秒内生成的两个GUID前10个字节完全相同,这几乎是不可能的!在SQL
Server中用SQL命令将这一思路实现出来便是:

DECLARE @aGuidUNIQUEIDENTIFIER

SET @aGuid =CAST(CAST(NEWID() AS BINARY(10))

+ CAST(GETDATE()AS BINARY(6)) AS UNIQUEIDENTIFIER)

经过测试,使用COMB做主键比使用INT做主键,在检索、插入、更新、删除等操作上仍然显慢,但比Unidentifier类型要快上一些。

以上是对SQL数据库中的主键与外键的简单介绍,如果有出入,还请谅解!

2015-10-28

时间: 2024-10-28 10:31:57

SQL数据库中的主键与外键的介绍的相关文章

数据库中为什么不推荐使用外键约束

数据库中为什么不推荐使用外键约束 首先我们明确一点,外键约束是一种约束,这个约束的存在,会保证表间数据的关系"始终完整".因此,外键约束的存在,并非全然没有优点. 作者:孤独烟来源:数据库开发|2018-11-29 14:30 收藏 分享 引言 其实这个话题是老生常谈,很多人在工作中确实也不会使用外键.包括在阿里的JAVA规范中也有下面这一条 [强制]不得使用外键与级联,一切外键概念必须在应用层解决. 但是呢,询问他们原因,大多是这么回答的 每次做DELETE 或者UPDATE都必须考

django rest framework 向数据库中插入数据时处理外键的方法

一.models.py中 from django.db import models class UserModel(models.Model) user_name = models.CharField() class MyModel(models.Model) author = models.Foreignkey(user) age = models.CharField() 二. 序列化文件 serializers.py 中创建序列化类 from rest_framework.serialize

【数据库】主键,外键,主表,从表,关联表,父表,子表

转自:https://www.2cto.com/database/201707/662425.html 一.前言 在数据库设计中,hibernate,iBatis等ORM框架的使用中经常听说主键,外键,主表,从表,关联表,父表,子表之类的术语,弄懂它们之前的区别与联系对于数据库设计和ORM框架的学习使用是非常有必要的. 二.概述 下面从数据库设计角度,ORM框架使用(以Hibernate为例),PowerDesigner软件以及实际业务角度进行一下介绍. (1) 数据库角度而言 主键:一般情况下

主键和外键约束(主表与从表)

通过上一篇随笔,笔者了解到,实体完整性是通过主键约束实现的,而参照完整性是通过外键约束实现的,两者都是为了保证数据的完整性和一致性. 主键约束比较好理解,就是主键值不能为空且不重复,已经强调好多次,所以这里重点记录对外键约束的学习. 主表与从表 若同一个数据库中,B表的外键与A表的主键相对应,则A表为主表,B表为从表. 假设学生表(学号,姓名,性别,专业号),专业表(专业号,专业名称),则学生表中的专业号为学生表的外键,其与专业表中"专业号"属性相关联,因此,专业表为主表,学生表为从表

数据库中主键与外键

在关系型数据库中,数据结构有逻辑结构和物理结构.物理结构指存储在物理介质上的数据文件的结构.逻辑结构即关系,也就是一张张的二维表.表中的一列即为一个字段(属性),代表的是实体的一个属性.表中的一行即为一条记录. 如:学生表中(学号,姓名,年龄,性别),在该表中有4个字段,代表学生实体的4个属性.表中的一行数据(001,张三,男,20),即一条记录,表示的是张三这个学生的信息.    在表中,用来唯一标识一条记录的字段集,叫做主关键字或者主关键码,简称主键(主码),而主键包含的属性(字段)叫做主属

数据库中的主键和外键

关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键 比如 学生表(学号 姓名 性别 班级) 其中每个学生的学号是唯一的,学号就是一个主键 课程表(课程编号,课程名,学分) 其中课程编号是唯一的,课程编号就是一个主键 成绩表(学号,课程号,成绩) 成绩表中单一一个属性无法唯一标识一条记录学号和课程号的组合才可以唯一标识一条记录,所以学号和课程号的属性组是一个主键 成绩表中的学号不是成绩表的主键但它和学生表中的学号相对应并且学生表中

Chapter 1. 数据库概述、主键、外键

数据库 database: 存储数据的仓库,用表来分类数据 特点:海量存储:查找速度快:并发性问题的控制:安全性:数据完整性(保存在数据库中的数据是正确的真实的) 数据库软件:DBSM   database management system 常见数据库软件:MySQL  MSSQL server  Oracle  Access SQL:Structured Query Language 结构化查询语言   特点:语言简洁.易学易用. SQL Server:是一种基于网络的大型数据库软件.主要用

SQL的主键和外键的作用

SQL的主键和外键的作用: 外键取值规则:空值或参照的主键值. (1)插入非空值时,如果主键表中没有这个值,则不能插入. (2)更新时,不能改为主键表中没有的值. (3)删除主键表记录时,你可以在建外键时选定外键记录一起级联删除还是拒绝删除. (4)更新主键记录时,同样有级联更新和拒绝执行的选择. 简而言之,SQL的主键和外键就是起约束作用. 关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键. 比如: 学生表(学号,姓名,性别,

SQL的主键和外键

SQL的主键和外键的作用: 外键取值规则:空值或参照的主键值. (1)插入非空值时,如果主键表中没有这个值,则不能插入. (2)更新时,不能改为主键表中没有的值. (3)删除主键表记录时,你可以在建外键时选定外键记录一起级联删除还是拒绝删除. (4)更新主键记录时,同样有级联更新和拒绝执行的选择. 简而言之,SQL的主键和外键就是起约束作用. 关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键. 比如: 学生表(学号,姓名,性别,