主键与外键ITeye - 牛牛娱乐

主键与外键ITeye

2019年02月23日15时24分34秒 | 作者: 涵菱 | 标签: 主键,数据库,数据 | 浏览: 1871

一、什么是主键、外键:

联络型数据库中的一条记载中有若干个特点,若其间某一个特点组(留意是组)能仅有标识一条记载,该特点组就能够成为一个主键
比方 
学生表(学号,名字,性别,班级)
其间每个学生的学号是仅有的,学号就是一个主键
课程表(课程编号,课程名,学分)
其间课程编号是仅有的,课程编号就是一个主键
成果表(学号,课程号,成果)
成果表中单一一个特点无法仅有标识一条记载,学号和课程号的组合才干够仅有标识一条记载,所以 学号和课程号的特点组是一个主键
 
成果表中的学号不是成果表的主键,但它和学生表中的学号相对应,而且学生表中的学号是学生表的主键,则称成果表中的学号是学生表的外键
 
同理 成果表中的课程号是课程表的外键
 
界说主键和外键首要是为了保护联络数据库的完整性,总结一下:
主键是能断定一条记载的仅有标识,比方,一条记载包括身份正号,名字,年纪。身份证号是仅有能断定你这个人的,其他都或许有重复,所以,身份证号是主键。
外键用于与另一张表的相关。是能断定另一张表记载的字段,用于坚持数据的一致性。比方,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]

@KeyName char(10),
@KeyValue int OUTPUT

AS
UPDATE IntKey SET @KeyValue = KeyValue = KeyValue + 1 WHERE KeyName = @KeyName
GO
这样,通过调用存储进程,咱们能够取得最新键值,保证不会呈现重复。若将OrderID字段设置为手动添加型字段,咱们的程序能够由以下几步来完结:首要调用存储进程,取得一个OrderID,然后运用这个OrderID填充Order表与OrderDetail表,最终在业务保护下对两表进行更新。

运用手动添加型字段作为主键在进行数据库间数据仿制时,能够保证数据兼并进程中不会呈现键值抵触,只需咱们为不同的数据库分配不同的主键取值段就行了。可是,运用手动添加型字段会添加网络的RoundTrip,咱们有必要通过添加一次数据库访问来获取当时主键键值,这会添加网络和数据库的负载,当处于一个低速或断开的网络环境中时,这种做法会有很大的坏处。一起,手艺保护主键还要考虑并发抵触等种种要素,这更会添加体系的杂乱程度。

3运用UniqueIdentifier

SQL 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 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类型要快上一些。关于测验数据能够参阅我2004年的漫笔。

除了运用存储进程完结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;

}

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表牛牛娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章

阅读排行

  • 1

    主键与外键ITeye

    主键,数据库,数据
  • 2

    Oracle学习笔记(三)ITeye

    用户,权限,命令
  • 3

    mysql大数据量导出运用ITeye

    企图,运用,成果
  • 4
  • 5

    数据库连接池完成原理ITeye

    数据库,运用,体系
  • 6

    数据库连接符ITeye

    联系,区别,数据库
  • 7

    Informix ODBC设置ITeye

    东西,装备,官方网站
  • 8

    ORACLE的阻隔等级ITeye

    业务,修正,句子
  • 9
  • 10