7 Data Integrity(数据完整性)

本章将介绍完整性约束如何实施与数据库相关联的业务规则,以及如何防止无效信息录入表中。

Introduction to Data Integrity(数据完整性简介)
数据保持数据完整性至关重要,数据完整性即遵循由数据库管理员或应用程序开发人员确定的业务规则。

Types of Integrity Constraints(完整性约束的类型)
Oracle数据库允许您在表级和列级应用约束。

States of Integrity Constraints(完整性约束的状态)
作为约束定义的一部分,您可以指定Oracle数据库应如何以及何时实施约束,从而确定约束状态。

另请参见:
有关列的背景信息以及完整性约束必要性的内容,请参阅“Tables Overview(表概述)”。

Introduction to Data Integrity(数据完整性简介)

数据保持数据完整性至关重要,数据完整性即遵循由数据库管理员或应用程序开发人员确定的业务规则。

业务规则规定了必须始终为真或必须始终为假的条件和关系。例如,每家公司都会针对薪资、员工编号、库存跟踪等方面制定自己的政策。

  • Techniques for Guaranteeing Data Integrity(确保数据完整性的技术)
    在设计数据库应用程序时,开发人员有多种方法可确保存储在数据库中的数据的完整性。

  • Advantages of Integrity Constraints(完整性约束的优势)
    完整性约束是一种模式对象,可使用SQL创建和删除。要实施数据完整性,应尽可能使用完整性约束。

Techniques for Guaranteeing Data Integrity(确保数据完整性的技术)

在设计数据库应用程序时,开发人员有多种方法可确保存储在数据库中的数据的完整性。

这些方法包括:

  • 使用触发的存储数据库过程实施业务规则
  • 使用存储过程完全控制对数据的访问
  • 在数据库应用程序的代码中实施业务规则
  • 使用Oracle数据库完整性约束,即在列级或对象级定义的、用于限制数据库中值的规则

另请参见:

  • “Overview of Triggers(触发器概述)”介绍了触发器的用途和类型
  • “Introduction to Server-Side Programming(服务器端编程简介)”介绍了存储过程的用途和特点

Advantages of Integrity Constraints(完整性约束的优势)

完整性约束是一种模式对象,可使用SQL创建和删除。要实施数据完整性,应尽可能使用完整性约束。

与其他实施数据完整性的方法相比,完整性约束具有以下优势:

  • 声明式便捷性(Declarative ease)
    由于您使用SQL语句定义完整性约束,因此在定义或修改表时无需额外编程。SQL语句易于编写,且可避免编程错误。

  • 集中化规则(Centralized rules)
    完整性约束是为表定义的,并存储在数据字典中。因此,所有应用程序录入的数据都必须遵循相同的完整性约束。如果在表级修改规则,应用程序无需更改。此外,应用程序可以使用数据字典中的元数据立即向用户提示违规情况,甚至在数据库检查SQL语句之前就能做到。

  • 数据加载时的灵活性(Flexibility when loading data)
    您可以暂时禁用完整性约束,以避免在加载大量数据时产生性能开销。数据加载完成后,您可以重新启用完整性约束。

另请参见:

  • “Overview of the Data Dictionary(数据字典概述)”
  • 有关如何维护数据完整性的内容,请参阅《Oracle Database Get Started with Oracle Database Development》和《Oracle Database Development Guide》
  • 有关如何管理完整性约束的内容,请参阅《Oracle Database Administrator’s Guide》

Types of Integrity Constraints(完整性约束的类型)

Oracle数据库允许您在表级和列级应用约束。

在列或属性的定义中指定的约束称为内联指定(inline specification)。在表定义中指定的约束称为外联指定(out-of-line specification)

键(key) 是包含在特定类型完整性约束定义中的一列或多列。键描述了关系型数据库中表与列之间的关系。键中的单个值称为键值(key values)

下表介绍了约束的类型。除了NOT NULL约束必须内联指定外,其他每种约束都既可以内联指定,也可以外联指定。

Table 7-1 Types of Integrity Constraints(表7-1 完整性约束的类型)

约束类型(Constraint Type) 描述(Description) 另请参见(See Also)
NOT NULL 允许或禁止在指定列中包含空值的行进行插入或更新操作。 “NOT NULL Integrity Constraints(NOT NULL完整性约束)”
唯一键(Unique key) 禁止多行在同一列或多列组合中具有相同值,但允许某些值为空。 “Unique Constraints(唯一约束)”
主键(Primary key) 结合了NOT NULL约束和唯一约束。它禁止多行在同一列或多列组合中具有相同值,并且禁止值为空。 “Primary Key Constraints(主键约束)”
外键(Foreign key) 将某一列指定为外键,并在该外键与一个主键或唯一键(称为引用键(referenced key))之间建立关系。 “Foreign Key Constraints(外键约束)”
检查(Check) 要求数据库中的值必须满足指定条件。 “Check Constraints(检查约束)”
REF 规定对REF列中的值允许执行的数据操作类型,以及这些操作对相关值的影响。在对象关系型数据库中,名为REF的内置数据类型封装了对指定对象类型的行对象的引用。REF列上的引用完整性约束可确保REF所引用的行对象存在。 有关REF约束的内容,请参阅《Oracle Database Object-Relational Developer’s Guide》
  • NOT NULL Integrity Constraints(NOT NULL完整性约束)
    NOT NULL约束要求表的某一列不包含空值。空值(null) 指不存在值的情况。默认情况下,表中的所有列都允许为空。

  • Unique Constraints(唯一约束)
    唯一键约束要求一列或多列中的每个值都是唯一的。对于具有唯一键约束的表,其任意两行在单个列(唯一键)或多列组合(复合唯一键)中都不能有重复值。

  • Primary Key Constraints(主键约束)
    在主键约束中,受该约束作用的一列或多列中的值可唯一标识一行数据。每个表只能有一个主键,主键实际上相当于为行命名,并确保不存在重复行。

  • Foreign Key Constraints(外键约束)
    只要两个表包含一列或多列公共列,Oracle数据库就可以通过外键约束(也称为引用完整性约束)来实施这两个表之间的关系。

  • Check Constraints(检查约束)
    对一列或多列施加的检查约束要求每一行中的指定条件都为真或未知。

  • 可预检查的检查约束(Precheckable CHECK Constraints)
    标记为PRECHECK的检查约束可以在数据库外部进行检查。

另请参见:
“Overview of Tables(表概述)”
有关约束类型的更多内容,请参阅《Oracle Database SQL Language Reference》

NOT NULL Integrity Constraints(NOT NULL完整性约束)

NOT NULL约束要求表的某一列不包含空值。空值指不存在值的情况。默认情况下,表中的所有列都允许为空。

NOT NULL约束适用于必须包含值的列。例如,hr.employees表要求email列必须有值。如果尝试插入一条没有电子邮件地址的员工记录,将会产生错误:

SQL> INSERT INTO hr.employees (employee_id, last_name) values (999, 'Smith');1 行出现错误:
ORA-01400: 无法将 NULL 插入 ("HR"."EMPLOYEES"."EMAIL")

只有当表中没有任何行,或者您指定了默认值时,才能添加带有NOT NULL约束的列。

另请参见:

  • 有关向表添加NOT NULL约束的示例,请参阅《Oracle Database Get Started with Oracle Database Development》
  • 有关使用NOT NULL约束的限制,请参阅《Oracle Database SQL Language Reference》
  • 有关何时使用NOT NULL约束的内容,请参阅《Oracle Database Development Guide》

Unique Constraints(唯一约束)

唯一键约束要求一列或多列中的每个值都是唯一的。对于具有唯一键约束的表,其任意两行在单个列(唯一键)或多列组合(复合唯一键)中都不能有重复值。

注意:
“键(key)”一词仅指在完整性约束中定义的列。由于数据库通过在键列上隐式创建或重用索引来实施唯一约束,因此“唯一键(unique key)”一词有时会被错误地用作“唯一键约束(unique key constraint)”或“唯一索引(unique index)”的同义词。

唯一键约束适用于不允许出现重复值的任何列。唯一约束与主键约束不同,主键约束的目的是唯一标识表中的每一行,其值通常除了具有唯一性外没有其他意义。唯一键的示例包括:

  • 客户电话号码(主键为客户编号)
  • 部门名称(主键为部门编号)

如示例4-1所示,hr.employees表的email列上存在唯一键约束。该语句的相关部分如下:

CREATE TABLE employees ( ... , email VARCHAR2(25)
CONSTRAINT emp_email_nn NOT NULL ...
, CONSTRAINT emp_email_uk UNIQUE (email) ... );
(…
email CONSTRAINT
,CONSTRAINT

emp_email_uk约束确保不会有两名员工拥有相同的电子邮件地址,如下例所示:

SQL> SELECT employee_id, last_name, email FROM employees WHERE email = 'PFAY';
EMPLOYEE_ID LAST_NAME EMAIL
EMAIL
202 Fay PFAY

SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) 
1 VALUES (999,'Fay','PFAY',SYSDATE,'ST_CLERK');

错误(ERROR):第1行出现错误:
ORA-00001: 违反唯一约束条件 (HR.EMP_EMAIL_UK)

除非同时定义了NOT NULL约束,否则空值始终满足唯一键约束。因此,同时具有唯一键约束和NOT NULL约束的列是很常见的。这种组合强制用户在唯一键中输入值,并消除了新行数据与现有行数据发生冲突的可能性。

注意:
由于多列上唯一键约束的搜索机制,对于部分为空的复合唯一键约束,其非空列中不能有相同的值。

Example 7-1 Unique Constraint(示例7-1 唯一约束)

SQL> SELECT employee_id, last_name, email FROM employees WHERE email = 'PFAY';
EMPLOYEE_ID LAST_NAME EMAIL
EMAIL
202 Fay PFAY

SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) 
1 VALUES (999,'Fay','PFAY',SYSDATE,'ST_CLERK');

错误(ERROR):第1行出现错误: ORA-00001: 违反唯一约束条件 (HR.EMP_EMAIL_UK)

另请参见:

  • “Unique and Nonunique Indexes(唯一索引和非唯一索引)”
  • 有关向表添加唯一约束的示例,请参阅《Oracle Database 2 Day Developer’s Guide》

Primary Key Constraints(主键约束)

在主键约束中,受该约束作用的一列或多列中的值可唯一标识一行数据。每个表只能有一个主键,主键实际上相当于为行命名,并确保不存在重复行。

主键可以是自然键(natural key),也可以是代理键(surrogate key)。自然键是由表中现有属性构成的有意义标识符。例如,查找表中的邮政编码可以作为自然键。与之相反,代理键是系统生成的递增标识符,用于确保表内的唯一性。通常,序列(sequence)用于生成代理键。

Oracle数据库对主键约束的实现可确保以下表述成立:

  • 任意两行在指定的一列或多列中都不会有重复值。
  • 主键列不允许为空。

需要使用主键的典型场景是员工的数字标识符。每个员工都必须有唯一的ID,且employees表中只能有一行数据描述该员工。

“Unique Constraints(唯一约束)”中的示例表明,现有员工的员工ID为202(员工ID是主键)。以下示例展示了尝试添加具有相同员工ID的员工以及没有员工ID的员工时的情况:

SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, 
job_id) 1 VALUES (202,'Chan','JCHAN',SYSDATE,'ST_CLERK');

错误(ERROR):第1行出现错误:
ORA-00001: 违反唯一约束条件 (HR.EMP_EMP_ID_PK)

SQL> INSERT INTO employees (last_name) VALUES ('Chan');

错误(ERROR):第1行出现错误: ORA-01400: 无法将 NULL 插入 (“HR”.“EMPLOYEES”.“EMPLOYEE_ID”)

数据库通过索引来实施主键约束。通常,为某一列创建主键约束时,会隐式创建一个唯一索引和一个NOT NULL约束。请注意以下例外情况:

  • 在某些情况下(例如创建带有可延迟约束的主键时),生成的索引不是唯一的。

注意:
您可以使用CREATE UNIQUE INDEX语句显式创建唯一索引。

  • 如果在创建主键约束时存在可用的索引,那么该约束会重用此索引,而不会隐式创建新索引。

默认情况下,隐式创建的索引名称与主键约束名称相同。您也可以为索引指定用户定义的名称。通过在用于创建约束的CREATE TABLE或ALTER TABLE语句中包含ENABLE子句,您可以为索引指定存储选项。

另请参见:
有关如何向表添加主键约束的内容,请参阅《Oracle Database Get Started with Oracle Database Development》和《Oracle Database Development Guide》

Foreign Key Constraints(外键约束)

只要两个表包含一列或多列公共列,Oracle数据库就可以通过外键约束(也称为引用完整性约束)来实施这两个表之间的关系。

外键约束要求:对于在该约束所定义的列中的每个值,另一个指定表和指定列中的值必须与之匹配。引用完整性规则的一个示例是:员工只能在现有的部门中工作。

下表列出了与引用完整性约束相关的术语。

Table 7-2 Referential Integrity Constraint Terms(表7-2 引用完整性约束术语)

术语(Term) 定义(Definition)
外键(Foreign key) 约束定义中包含的、引用某个引用键的一列或多列。例如,employees表中的department_id列是外键,它引用departments表中的department_id列。
外键可以定义为多列。但是,复合外键必须引用具有相同列数和相同数据类型的复合主键或复合唯一键。
外键的值可以与引用的主键或唯一键的值匹配,也可以为空。如果复合外键中的任意一列为空,那么该键的非空部分不必与父键的对应部分匹配。
引用键(Referenced key) 被外键引用的表的唯一键或主键。例如,departments表中的department_id列是employees表中department_id列所引用的引用键。
依赖表或子表(Dependent or child table) 包含外键的表。该表依赖于引用的唯一键或主键中存在的值。例如,employees表是departments表的子表。
引用表或父表(Referenced or parent table) 被子表的外键引用的表。正是该表的引用键决定了子表中是否允许进行特定的插入或更新操作。例如,departments表是employees表的父表。

图7-1展示了employees表的department_id列上的外键。该外键确保该列中的每个值都必须与departments表的department_id列中的某个值匹配。这样,employees表的department_id列中就不会出现无效的部门编号。

Figure 7-1 Referential Integrity Constraints(图7-1 引用完整性约束)

自引用完整性约束(Self-Referential Integrity Constraints)
自引用完整性约束是一种引用同一表中父键的外键。

空值与外键(Nulls and Foreign Keys)
关系模型允许外键的值要么与引用的主键或唯一键的值匹配,要么为空。例如,hr.employees表中的某一行可能未指定部门ID。

父键修改与外键(Parent Key Modifications and Foreign Keys)
外键与父键之间的关系会对父键的删除产生影响。例如,如果用户尝试删除某个部门的记录,那么该部门中员工的记录会如何处理?

索引与外键(Indexes and Foreign Keys)
通常,外键应该建立索引。唯一的例外情况是:与之匹配的唯一键或主键从不进行更新或删除操作。

另请参见:
有关如何向外键添加外键约束的内容,请参阅《Oracle Database Get Started with Oracle Database Development》和《Oracle Database Development Guide》

自引用完整性约束(Self-Referential Integrity Constraints)

自引用完整性约束是一种引用同一表中父键的外键。

在下图中,自引用约束确保employees表的manager_id列中的每个值都对应于该表employee_id列中的某个现有值。例如,员工102的经理必须存在于employees表中。该约束可避免manager_id列中出现无效的员工编号。

Figure 7-2 Single Table Referential Constraints(图7-2 单表引用约束)

空值与外键(Nulls and Foreign Keys)

关系模型允许外键的值要么与引用的主键或唯一键的值匹配,要么为空。例如,hr.employees表中的某一行可能未指定部门ID。

如果复合外键中的任意一列为空,那么该键的非空部分不必与父键的对应部分匹配。例如,预订表(reservations table)的table_id列和date列上可能有一个复合外键,但table_id列的值为空。

父键修改与外键(Parent Key Modifications and Foreign Keys)

外键与父键之间的关系会对父键的删除产生影响。例如,如果用户尝试删除某个部门的记录,那么该部门中员工的记录会如何处理?

当父键被修改时,引用完整性约束可以指定对字表中的相关行执行以下操作:

  • 删除或更新时不执行操作(No action on deletion or update)
    在正常情况下,如果修改引用键值会违反引用完整性,用户则无法进行修改。例如,如果employees表的department_id列是引用departments表的外键,且有员工属于某个特定部门,那么尝试删除该部门的行就会违反约束。

  • 级联删除(Cascading deletions)
    当包含引用键值的行被删除时,删除操作会级联(DELETE CASCADE),导致子表中所有包含相关外键值的行也被删除。例如,删除departments表中的某一行,会导致该部门中所有员工的行也被删除。

  • 设置为空的删除(Deletions that set null)
    当包含引用键值的行被删除时,删除操作会将相关值设为null(DELETE SET NULL),导致子表中所有包含相关外键值的行将这些值设为null。例如,删除某个部门的行,会将该部门中员工的department_id列值设为null。

表7-3概述了针对父表中的键值和子表中的外键值执行不同引用操作时所允许的DML语句。

Table 7-3 DML Statements Allowed by Update and Delete No Action(表7-3 “更新时不执行操作”和“删除时不执行操作”所允许的DML语句)

DML语句(DML Statement) 对父表执行(Issued Against Parent Table) 对子表执行(Issued Against Child Table)
INSERT 如果父键值唯一,则始终允许执行 仅当外键值存在于父键中,或部分为空、全部为空时,才允许执行
UPDATE NO ACTION(更新时不执行操作) 仅当语句不会导致子表中存在没有引用父键值的行时,才允许执行 仅当新的外键值仍引用某个引用键值时,才允许执行
DELETE NO ACTION(删除时不执行操作) 仅当子表中没有行引用该父键值时,才允许执行 始终允许执行

Table 7-3 (Cont.) DML Statements Allowed by Update and Delete No Action(表7-3(续)“更新时不执行操作”和“删除时不执行操作”所允许的DML语句)

DML语句(DML Statement) 对父表执行(Issued Against Parent Table) 对子表执行(Issued Against Child Table)
DELETE CASCADE(级联删除) 始终允许执行 始终允许执行
DELETE SET NULL(删除时设为null) 始终允许执行 始终允许执行

注意: Oracle数据库的外键完整性约束不支持的其他引用操作,可以使用数据库触发器来实施。请参阅“Overview of Triggers(触发器概述)”。

有关ON DELETE子句的内容,请参阅《Oracle Database SQL Language Reference》

索引与外键(Indexes and Foreign Keys)

通常,外键应该建立索引。唯一的例外情况是:与之匹配的唯一键或主键从不进行更新或删除操作。

在子表的外键上建立索引具有以下好处:

  • 避免对子表进行全表锁定。相反,数据库会对索引获取行锁。
  • 无需对子表进行全表扫描。例如,假设用户从departments表中删除了部门10的记录。如果未对employees表的department_id列建立索引,那么数据库必须扫描employees表,以确认该部门中是否存在员工。

另请参见:

  • 有关已建立索引和未建立索引的外键列的锁定行为,请参阅“Locks and Foreign Keys(锁与外键)”
  • 有关索引的用途和特点,请参阅“Introduction to Indexes(索引简介)”

Check Constraints(检查约束)

对一列或多列施加的检查约束要求每一行中的指定条件都为真或未知。

如果DML操作导致约束条件的计算结果为假,那么该SQL语句将回滚。检查约束的主要好处是能够实施非常具体的完整性规则。例如,您可以在hr.employees表中使用检查约束来实施以下规则:

  • salary列的值不得大于10000。
  • commission列的值不得大于salary列的值。

以下示例在employees表上创建了一个薪资最大值约束,并展示了当尝试插入一条薪资超过最大值的行时的情况:

SQL> ALTER TABLE employees ADD CONSTRAINT max_emp_sal CHECK (salary < 10001);
SQL> INSERT INTO employees 
(employee_id,last_name,email,hire_date,job_id,salary)
1 VALUES (999,'Green','BGREEN',SYSDATE,'ST_CLERK',20000);

错误(ERROR):第1行出现错误:
ORA-02290: 违反检查约束条件 (HR.MAX_EMP_SAL)

单个列可以有多个在其定义中引用该列的检查约束。例如,salary列可以有一个禁止值超过10000的约束,还有一个禁止值小于500的单独约束。

如果某一列存在多个检查约束,则这些约束的设计必须确保其用途不冲突。无法假定条件的评估顺序。数据库不会验证检查条件是否互斥。

另请参见:
有关检查约束的限制,请参阅《Oracle Database SQL Language Reference》

Precheckable CHECK Constraints(可预检查的检查约束)

标记为PRECHECK的检查约束可以在数据库外部进行检查。

在创建或修改表时,可以将检查约束设置为PRECHECK状态。PRECHECK状态是对约束的ENABLE(启用)和VALIDATE(验证)状态的补充,一个约束可以同时处于这三种状态。

如果您使用JSON来表示检查约束,Oracle数据库允许您导出JSON模式,并在应用程序客户端内使用JSON模式验证器来验证JSON数据。当约束被设置为PRECHECK状态时,表示该约束有一个等效的JSON模式,该模式保留了约束的语义。客户端应用程序开发人员可以在客户端应用程序内对约束进行预验证。数据库会检查该约束,如果无法用JSON模式表示该约束,则会生成错误。

相关主题(Related Topics)

  • 使用PRECHECK和CHECK约束对JSON数据进行预验证(Using PRECHECK with CHECK Constraint to prevalidate JSON Data)
  • ALL_CONSTRAINTS

States of Integrity Constraints(完整性约束的状态)

作为约束定义的一部分,您可以指定Oracle数据库应如何以及何时实施约束,从而确定约束状态。

对修改数据和现有数据的检查(Checks for Modified and Existing Data)
数据库允许您指定约束是适用于现有数据还是未来数据。如果约束处于启用状态,那么数据库会在录入或更新新数据时对其进行检查。不符合约束要求的数据无法录入数据库。

数据库何时检查约束的有效性(When the Database Checks Constraints for Validity)
每个约束要么处于不可延迟(not deferrable,默认)状态,要么处于可延迟(deferrable)状态。此状态决定了Oracle数据库何时检查约束的有效性。

约束检查示例(Examples of Constraint Checking)
以下示例有助于说明Oracle数据库何时执行约束检查。

对修改数据和现有数据的检查(Checks for Modified and Existing Data)

数据库允许您指定约束是适用于现有数据还是未来数据。如果约束处于启用状态,那么数据库会在录入或更新新数据时对其进行检查。不符合约束要求的数据无法录入数据库。

例如,在employees表的department_id列上启用NOT NULL约束,可确保未来的每一行都有部门ID。如果约束处于禁用状态,那么表中可能包含违反该约束的行。

您可以将约束设置为以下两种验证模式之一:

  • VALIDATE(验证)
    现有数据必须符合约束要求。例如,在employees表的department_id列上启用NOT NULL约束并将其设置为VALIDATE模式,会检查所有现有行是否都有部门ID。

  • NOVALIDATE(不验证)
    现有数据无需符合约束要求。实际上,这是一种“相信我”模式。例如,如果您确定加载到表中的每笔销售记录都有日期,那么您可以在日期列上创建NOT NULL约束,并将该约束设置为NOVALIDATE模式。未实施的约束通常仅在物化视图和查询重写中有用。

对于处于NOVALIDATE模式的约束,RELY参数表示优化器可以使用该约束来确定连接信息。即使该约束不用于验证数据,它也能为物化视图实现更复杂的查询重写,并允许数据仓库工具从数据字典中检索约束信息。默认值为NORELY,表示优化器实际上不会感知到该约束。

VALIDATE和NOVALIDATE的行为始终取决于约束是启用还是禁用。下表总结了它们之间的关系。

Table 7-4 Checks on Modified and Existing Data(表7-4 对修改数据和现有数据的检查)

修改数据(Modified Data) 现有数据(Existing Data) 总结(Summary)
ENABLE(启用) VALIDATE(验证) 现有数据和未来数据都必须遵守约束。如果尝试将新约束应用于已填充数据的表,而现有行违反该约束,则会产生错误。
ENABLE(启用) NOVALIDATE(不验证) 数据库会检查约束,但不要求所有行都满足该约束。因此,现有行可能违反约束,但新行或修改后的行必须遵守规则。这种模式通常用于数据仓库,这些数据仓库中包含的现有数据的完整性已得到验证。
DISABLE(禁用) VALIDATE(验证) 数据库禁用约束,删除其索引,并禁止修改受约束的列。
DISABLE(禁用) NOVALIDATE(不验证) 不检查约束,且约束也不一定成立。

另请参见:
有关约束状态的内容,请参阅《Oracle Database SQL Language Reference》

数据库何时检查约束的有效性(When the Database Checks Constraints for Validity)

每个约束要么处于不可延迟(not deferrable,默认)状态,要么处于可延迟(deferrable)状态。此状态决定了Oracle数据库何时检查约束的有效性。

下图展示了可延迟约束的选项。

Figure 7-3 Options for Deferrable Constraints(图7-3 可延迟约束的选项)

不可延迟约束(Nondeferrable Constraints)
对于不可延迟约束,Oracle数据库绝不会将约束的有效性检查延迟到事务结束时进行。相反,数据库会在每条语句执行结束时检查约束。如果违反约束,语句将回滚。

可延迟约束(Deferrable Constraints)
可延迟约束允许事务使用SET CONSTRAINT子句将约束检查延迟到执行COMMIT语句时进行。如果您对数据库所做的修改可能违反约束,那么此设置实际上允许您在完成所有修改之前禁用约束。

不可延迟约束(Nondeferrable Constraints)

对于不可延迟约束,Oracle数据库绝不会将约束的有效性检查延迟到事务结束时进行。相反,数据库会在每条语句执行结束时检查约束。如果违反约束,语句将回滚。

例如,employees表的last_name列上存在一个不可延迟的NOT NULL约束。如果某个会话尝试插入一条没有姓氏的行,那么数据库会立即回滚该语句,因为违反了NOT NULL约束,不会插入任何行。

可延迟约束(Deferrable Constraints)

可延迟约束允许事务使用SET CONSTRAINT子句将约束检查延迟到执行COMMIT语句时进行。如果您对数据库所做的修改可能违反约束,那么此设置实际上允许您在完成所有修改之前禁用约束。

您可以设置数据库检查可延迟约束的默认行为。您可以指定以下两个属性之一:

  • INITIALLY IMMEDIATE(初始立即检查)
    数据库在每条语句执行后立即检查约束。如果违反约束,数据库会回滚该语句。

  • INITIALLY DEFERRED(初始延迟检查)
    数据库在执行COMMIT语句时检查约束。如果违反约束,数据库会回滚事务。

假设employees表的last_name列上有一个可延迟的NOT NULL约束,并设置为INITIALLY DEFERRED(初始延迟检查)。某用户创建了一个包含100条INSERT语句的事务,其中部分语句的last_name值为空。当该用户尝试提交时,数据库会回滚所有100条语句。但是,如果该约束设置为INITIALLY IMMEDIATE(初始立即检查),数据库则不会回滚事务。

如果某个约束会触发某个操作,那么无论该约束是延迟的还是立即的,数据库都会将此操作视为触发该操作的语句的一部分。例如,删除departments表中的某一行,会导致删除employees表中所有引用该已删除部门行的行。在这种情况下,从employees表中删除行被视为对departments表执行DELETE语句的一部分。

另请参见:
有关约束属性及其默认值的内容,请参阅《Oracle Database SQL Language Reference》

约束检查示例(Examples of Constraint Checking)

以下示例有助于说明Oracle数据库何时执行约束检查。

假设有以下情况:

  • employees表具有“Self-Referential Integrity Constraints(自引用完整性约束)”中所示的结构。
  • 该自引用约束使manager_id列中的值依赖于employee_id列中的值。
示例:在外键列中插入不存在父键值的值(Example: Insertion of a Value in a Foreign Key Column When No Parent Key Value Exists)

本示例涉及向employees表中插入第一行数据。由于当前不存在任何行,那么如果manager_id列中的值无法引用employee_id列中的现有值,该如何录入行呢?

可能的方法有:

  1. 如果manager_id列没有定义NOT NULL约束,那么您可以为第一行的manager_id列录入空值。由于外键允许为空,Oracle数据库会将该行插入表中。
  2. 您可以在employee_id列和manager_id列中录入相同的值,表示该员工是自己的经理。

这种情况表明,Oracle数据库会在语句执行后执行约束检查。为了允许录入父键和外键值相同的行,数据库必须先插入新行,然后再确定表中是否存在employee_id与新行的manager_id对应的行。
3. 多行INSERT语句(例如包含嵌套SELECT语句的INSERT语句)可以插入相互引用的行。

例如,第一行的员工ID(employee ID)可能为200,经理ID(manager ID)可能为300;而第二行的员工ID可能为300,经理ID可能为200。约束检查会延迟到INSERT语句完全执行后进行。数据库会插入所有行,然后检查所有行是否存在约束违规情况。

默认值会在语句解析之前作为INSERT语句的一部分包含在内。因此,列的默认值需要接受所有完整性约束检查。

示例:更新所有外键和父键值(Example: Update of All Foreign Key and Parent Key Values)

在本示例中,自引用约束使employees表的manager_id列中的值依赖于employee_id列中的值。

某公司被收购。由于此次收购,所有员工编号都必须更新为当前值加5000,以便与新公司的员工编号保持一致。如以下图形所示,部分员工同时也是经理:

Figure 7-4 The employees Table Before Updates(图7-4 更新前的employees表)

由于经理编号也是员工编号,因此经理编号也必须加5000。您可以执行以下SQL语句来更新这些值:

UPDATE employees SET employee_id = employee_id + 5000, manager_id = manager_id + 5000;

尽管定义了约束来验证每个manager_id值是否与某个employee_id值匹配,但上述语句仍然有效,因为数据库会在语句执行完成后实际执行约束检查。图7-5显示,数据库会在检查约束之前执行整个SQL语句的所有操作。

Figure 7-5 Constraint Checking(图7-5 约束检查)

本节中的示例说明了INSERT和UPDATE语句执行期间的约束检查机制,但数据库对所有类型的DML语句都使用相同的机制。而且,数据库对所有类型的约束(不仅仅是自引用约束)都使用相同的机制。

注意: 对视图或同义词执行的操作需要遵守基表上定义的完整性约束。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐