暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

数据完整性简介

2024-06-11
205

业务规则会指定一些条件和关系,它们要么必须始终为真,要么必须始终为假。例如,每个公司会定义其有关工资、雇员人数、库存跟踪,等方面的政策。保持数据的数据完整性很重要,它由数据库管理员或应用程序开发人员来确定,以遵从业务规则。
用于保证数据完整性的技术
在设计数据库应用程序时,开发人员有多种选项用于保证存储在数据库中的数据的完整性。这些选项包括:
•如"触发器概述"中所述,通过数据库触发器存储过程,强制实施业务规则
•如"服务器端编程介绍"中所述,使用存储过程完全控制数据访问
•在数据库应用程序的代码中执行业务规则
•使用Oracle完整性约束,它们是定义在列级或对象级上的,用来限制数据库中的值的规则
本章介绍完整性约束的基本概念。
完整性约束的优势
完整性约束是一个模式对象,它使用SQL来创建和删除。若要强制实施数据完整性,请尽可能使用完整性约束。相对于其他强制数据完整性替代方案,完整性约束包括如下优点:
•容易声明
在您定义或更改表时,使用SQL语句定义完整性约束,而无需任何额外的编程。SQL语句易于编写,也易于排除编程错误。
•集中化的规则
完整性约束定义在表上,并存储在数据字典中(请参阅"数据字典概述)。因此,由所有应用程序输入的数据都必须遵守相同的完整性约束。如果约束规则在表级发生了更改,应用程序不需要变更。此外,甚至在数据库检查SQL语句之前,应用程序就可以使用数据字典中的元数据立即告知用户的违例行为。
•加载数据时的灵活性
当加载大量数据时,您可以暂时禁用完整性约束,以避免性能开销。当数据加载完成后,您可以重新启用完整性约束。
完整性约束的类型
Oracle数据库使您能够在表级或列级应用约束。作为列或属性定义的一部分而指定的约束,称为行内规范约束。作为表定义的一部分而指定的约束称为行外规范约束。
好几种类型的完整性约束定义中都使用键这个术语。键是某种类型的完整性约束的定义中包含的列或列集。键描述关系数据库中的表与列之间的关系。键中的单个值称为键值。
表5-1描述了约束的类型。除NOTNULL必须指定为行内规范,其它每一个都可以指定为行内或行外规范。
image.png
image.png
非空完整性约束
NOTNULL约束要求表中的列不包含空值。空值即值的缺失。默认情况下,一个表中的所有列都允许空值。
NOTNULL约束主要用于不能缺少值的列。例如,hr.employees表在last_name列上需要有值。试图插入一个没有姓氏的雇员行会生成一个错误:
image.png
image.png
如果表已包含有任何行,则您不能添加一个具有NOTNULL约束的列,除非您还为其指定了DEFAULT子句
唯一性约束
唯一键约束要求在一个列或列集中的每个值是唯一的。在一个表中,不允许多个行在有唯一键约束的列(唯一键)或列集(复合唯一键)上具有重复值。
注意:
术语键仅指在完整性约束中定义的列。因为数据库通过在键列上隐含创建或重用索引来强制执行唯一性约束,术语唯一键有时会被错误地用作唯一键约束或唯一索引的同义词。
唯一键约束适合于任何不允许重复值的列。唯一约束与主键约束不同,主键的目的是唯一地标识表中的每一行,通常它只要求唯一,而并不一定要有什么实际意义。唯一键的示例包括:
•客户电话号码,其主键是客户号
•部门名称,其主键是部门编号
如示例2-1所示,在hr.employees表的email列上存在一个唯一键约束。相关部分的语句如下所示:
image.png
如示例5-1所示,emp_email_uk约束可以确保没有任何两名雇员具有相同的电子邮件地址。
image.png
除非也指定了NOTNULL约束,否则空值也始终满足唯一键约束。因此,典型的情况是在列上同时具有唯一键约束和非空约束。这种组合强制用户输入的是唯一值,并消除新行数据与现有行数据发生冲突的可能性。
注意:
鉴于在多个列上的唯一键约束的搜索机制,对含有部分空值的复合唯一键约束中,在非空列中不能有相同的值。
主键约束
在一个主键约束中的列或列集,其值能唯一地标识行。每个表只能有一个主键,起到确定行的作用,并确保不存在任何重复的行。
主键可以是自然键或代理键。自然键是由表中的现有属性组成的一个有意义的标识符。例如,一个自然键可能是查找表中的邮政编码。相比之下,代理键是一个系统生成的递增标识符,以确保在一个表中的唯一性。通常,由一个序列生成代理键。
Oracle数据库实现的主键约束可以保证如下行为:
•任何两行在指定的列或列集上都不具有重复值。
•主键列不允许空值。
需要用到主键的典型情况是雇员的编号标识。每个员工必须具有唯一的id。一名雇员只能由employees表中的有且仅有的一行来描述
示例5-1指示一个雇员ID为202的现有员工,其雇员ID是主键。下面的示例显示了试图添加一名具有相同雇员ID的雇员,和一名没有雇员ID的雇员:
image.png
数据库使用索引来强制主键约束。通常,在某列上创建的主键约束会隐含创建一个唯一索引和一个非空约束。但请注意,此规则有如下例外情况:
•有时,当您使用一个可延迟的约束选项来创建一个主键时,其生成的索引不是唯一的。
注意:
您可以使用CREATEUNIQUEINDEX语句,显式创建一个唯一索引。
•当你创建主键约束时,如果有一个现成的索引可用,则该主键约束会重用此索引,而不会隐式创建一个额外的新索引。
默认情况下,隐式创建的索引名称即是主键约束的名称。您也可以为索引指定一个用户定义的名称。您可以通过在用来创建该约束的CREATETABLE或ALTERTABLE语句中包括ENABLE子句,来为索引指定存储选项。
外键约束
只要两个表包含一个或多个公共列,则数据库可以通过一个外键约束(也称为参照完整性约束)来强制建立两个表之间的关系。该约束要求定义约束的列中的每个值,必须与另一个指定表中的指定列中的值相匹配。参照完整性规则的一个例子是,雇员只可以为一个现有的部门工作。
image.png

图5-1显示在employees.department_id列上的一个外键。它保证在此列中的每个值必须与departments.department_id列中的某个值相匹配。因此,在employees.department_id列中不会存在错误的部门编号。
图5-1引用完整性约束
image.png
自引用完整性约束
图5-2显示了一个自引用的完整性约束。在这种情况下,外键引用同一个表中的父键。
在图5-2中,参照完整性约束可以确保在employees.manager_id列中的每个值对应于employees.employee_id列中的一个现有值。例如,员工102的经理必须存在于employees表中。此约束消除了在manager_id列中存在错误的雇员人数的可能性。
image.png
空值和外键
关系模型允许外键的值可以匹配被引用主键或唯一键值,或者为空。例如,用户可以往hr.employees表插入行,而无需指定一个部门id。
如果一个复合外键的任何列为空,则该键的非空部分不一定要匹配父项中的任何相应部分。
父键修和外键
删除父键会影响外键和父键之间的关系。例如,如果用户试图删除某个部门的记录,那么属于这个部门的雇员记录会发生事情呢?
在父键被修改时,参照完整性约束可以指定在子表中的相关行上,执行以下某种操作之一:
•对删除或更新操作,不采取任何动作
在正常的情况下,如果结果会违反参照完整性,用户不能修改被引用的键值。例如,如果employees.department_id是对departments表的一个外键,且有一些雇员属于某个特定部门,则试图删除该部门所在的行会违反约束。
•级联删除
级联删除(DELETECASCADE)即是当包含被引用键值的行被删除时,导致子表中的所有的外键依赖值所在行也会被删除。例如,删除departments表中的某行,会导致这个部门中的所有雇员行也被删除。
•对删除置空
删除置空(DELETESETNULL)即是当包含被引用键值的行被删除时,导致子表中的所有的外键依赖值被全部置空。例如,删除部门表中的某行,会导致将该部门中的所有雇员行的department_id列被置为空。
表5-3列出了在父表中的键值及子表中的外键值上,针对不同的引用性操作,所允许的DML语句。
image.png
image.png
注意:
其他Oracle数据库外键完整性约束不支持的引用性动作,可以使用数据库触发器强制执行。请参阅"触发器概述"。
索引和外键
作为一条规则——总是应该为外键编制索引。唯一的例外是,当匹配的唯一键或主键永远不会被更新或删除时。在子表中的外键上建立索引提供了下列好处:
•可防止在子表上的全表锁定。相反,数据库只需要在索引上获取一个行锁。
•消除在子表上进行全表扫描的需要。作为一个说明,假定用户从部门表中删除部门10的记录。如果没有为employees.department_id建立索引,则数据库必须扫描employees表,以查看是否存在属于部门10的雇员。
检查性约束
在一个列或列集上的检查约束,要求所指定的条件为真,或对每一行来说是未知的。如果DML语句导致约束条件的计算结果为假,则SQL语句将被回滚。
检查约束的主要好处是,具备强制执行非常具体的完整性规则的能力。例如可以使用检查性约束在hr.employees表中强制执行下列规则:
•在salary列中不能有大于10000的值。
•commission列必须有一个值,但不能大于salary。
下面的示例在employees表上创建了一个最高薪金约束,并演示了当一个语句尝试插入一个其所包含的薪水超过了最大值的行时,会发生什么情况:
image.png
单个列可以在其定义中包括多个检查性约束。例如salary列可以有一个防止其值超过10000的约束,和另一个可以防止其值小于500的约束。
如果在某列上存在多个检查约束,则他们必须被合理设计,保证他们的目的不会发生冲突。不能假定多个条件计算之间的顺序。数据库不会验证这些检查条件是否相互排斥。
完整性约束的状态
作为约束定义的一部分,您可以指定数据库应如何及何时强制执行该约束条件,从而确定约束状态。
对现有数据和新数据的检查――针对数据
数据库使您可以指定将某个约束应用于现有数据还是应用于新数据。如果约束是启用的,则当输入或更新数据时,数据库会检查新的数据。不符合该约束的数据不能输入到数据库。例如,对employees.department_id启用NOTNULL约束,可以保证新插入的每一行都有一个部门id。如果约束是禁用的,则表可能会包含违反约束的行。
您可以设置约束来验证(VALIDATE)或不验证(NOVALIDATE)现有数据。如果指定了VALIDATE,则现有数据必须符合该约束。例如,对employees.department_id启用NOTNULL约束并将其设置为VALIDATE,则会检查每个现有行都有一个部门id。如果指定了NOVALIDATE,则现有数据不需要符合该约束。
VALIDATE和NOVALIDATE的行为都取决于约束是启用的还是禁用的。表5-4总结了这些选项组合。
image.png
image.png
可延迟约束――针对约束
约束可能处于不可延迟(默认值)或可延迟两种状态之一。该状态确定数据库何时检查约束的有效性。下图描述了可延迟约束的选项。
image.png
不可延迟约束
如果一个约束不可延迟,则Oracle数据库决不会将约束的有效性检查延迟到事务结束。相反,数据库在每个语句的结尾检查约束。如果违反了约束,则该语句被回滚。
例如,假设您在employees.last_name列上创建一个不可延迟的NULL约束。如果用户试图插入一个没有姓氏的行,则数据库会立即回滚该语句,因为违反了NOTNULL约束。在这种情况下,将不会插入任何行。
可延迟约束
可延迟约束允许事务使用SETCONSTRAINT子句将约束检查推迟到发出COMMIT语句时。如果你对数据库中做了可能违反约束的更改,则此设置让您有效地禁用该约束,直到完成所有更改。
您可以设置数据库检查可延迟约束时的默认行为。您可以指定下列属性之一:
•INITIALLYIMMEDIATE
image.png
•INITIALLYDEFERRED
发出COMMIT时,数据库检查约束。如果违反了约束,则数据库回滚该事务。
假定一个在employees.last_name列上的可延迟的NOTNULL约束被设置为INITIALLYDEFERRED。用户创建了一个包含100个INSERT语句的事务,其中一些在last_name列上具有空值。当用户尝试提交时,数据库将回滚所有100个语句。但是,如果这种限制被设置为INITIALLYIMMEDIATE,则数据库将不会回滚整个事务。(只会回滚那些含有NULL值的行)
如果一个约束导致一个动作,则数据库认为此动作是引发它的语句的一部分,而无论该约束是可延迟的或立即的。例如,从departments表中删除一行,会导致删除employees表中引用了相应的被删部门的雇员。在这种情况下,从employees表中删除雇员被认为是对departments表执行的DELETE语句的一部分。
约束检查的示例
一些例子可以帮助说明数据库何时执行检查约束。假定如下:
•employees表具有如图5-2所示的结构。
•自引用约束要求在manager_id列中的条目依赖employee_id列的值。
当父键值不存在时,往外键列中插入值
考虑向employees表中插入第一行的情况。由于当前不存在任何行,那么如果manager_id列中的值不能引用在employee_id列中的任何现有值,如何才能输入新行呢?一些可能情况是:
•如果在manager_id列上没有定义NOTNULL约束,可以为第一行中的manager_id列输入一个空值。因为在外键中允许使用空值,此行可以被插入到表中。
•如果该雇员是他或她自己的经理,可以在employee_id和manager_id列中输入相同的值。
这种情况表明数据库是在完全运行该语句之后,执行其约束检查的。若要允许输入在父键和外键中具有相同值的行,数据库必须首先运行该语句(也就是插入新的行),然后再确定表中是否有任何行其employee_id对应新行的manager_id列。
•一个多行的INSERT语句,如一个具有嵌套SELECT语句的INSERT语句,可以插入相互引用的多个行。
例如,第一行可能具有雇员ID200和经理ID300,而第二行的雇员ID300和经理ID200。约束检查被推迟到整个语句执行完成。首先,所有行先被插入,然后对所有行进行违反约束检查。
在语句进行解析之前,其默认值作为INSERT语句的一部分被包括在内。因此,默认列值都要遵从所有完整性约束检查。
同时更新外键和父键的值
考虑该自引用完整性约束在另一个不同场景中的情况。假如该公司已出售。由于这次出售,所有雇员编号必须都更新为其当前值加5000,以适应新公司的员工编号。因为经理编号是真正雇员编号(请参阅图5-3),经理编号也必须增加5000。
image.png
image.png
虽然已定义了一个约束,以验证每个manager_id值与employee_id值匹配,但前面的语句仍然是合法的,因为数据库在该语句完成后检查约束。图5-4显示,数据库在检查约束之前执行整个SQL语句的操作。
image.png
本节中的示例只说明了在INSERT和UPDATE语句执行过程中的约束检查机制,实际上,数据库在所有类型的DML语句中都使用相同的机制。相同的机制用于所有类型的约束,而不只是自引用约束。
注意:
对视图或同义词上的操作,需要遵从定义在基础表上的完整性约束。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论