数据完整性简介
用于保证数据完整性的技术
在设计数据库应用程序时,开发人员有多种选项用于保证存储在数据库中的数据的完整性。
这些选项包括:
- 通过数据库触发器存储过程, 强制实施业务规则
-
使用存储过程完全控制数据访问
-
在数据库应用程序的代码中执行业务规则
- 使用 Oracle 完整性约束,它们是定义在列级或对象级上的,用来限制数据库中的值的规则
另见:
-
"Overview of Triggers"解释触发器的用途和类型
-
"Introduction to Server-Side Programming"解释存储过程的用途和特征
完整性约束的优势
-
容易声明
在您定义或更改表时, 使用 SQL 语句定义完整性约束, 而无需任何额外的编程。 SQL 语句易于编写,也易于排除编程错误。 -
集中化的规则
完整性约束定义在表上,并存储在数据字典中。因此,由所有应用程序输入的数据都必须遵守相同的完整性约束。如果约束规则在表级发生了更改, 应用程序不需要变更。此外,甚至在数据库检查 SQL 语句之前, 应用程序就可以使用数据字典中的元数据立即告知用户的违例行为。
-
加载数据时的灵活性
当加载大量数据时, 您可以暂时禁用完整性约束, 以避免性能开销。当数据加载完成后, 您可以重新启用完整性约束。
另见:
完整性约束的类型
Oracle 数据库使您能够在表级或列级应用约束。
下表描述了约束的类型。 除 NOT NULL 必须指定为行内规范,其它每一个都可以指定为行内或行外规范。
表 5-1 约束的类型
| 约束类型 | 描述 | 另见 |
|---|---|---|
| |
允许或不允许在指定的列插入或更新包含空值的行。
| |
| Unique key |
在相同的列、或多个列的组合中,不允许多个行具有相同的值,但允许一些值为空。
| |
| Primary key |
合并 NOT NULL 约束和唯一性约束。 在相同的列、或多个列的组合中,不允许多个行具有相同的值,也不允许为空。
| |
| Foreign key |
指定一个列作为外键,在外键和主键或唯一键之间建立关系, 也称为引用键。
| |
| Check | 要求某个数据库值服从指定的条件。 | |
| |
规定对 REF 列中的值上的允许的数据操作类型,这些操作如何影响依赖值。 在一个对象-关系的数据库中, 一个叫做 REF 的内置数据类型, 封装了对一个某种指定对象类型的行对象的引用。 在 REF 列上的引用完整性约束确保该 REF 有一个行对象。
| Oracle Database Object-Relational Developer's Guide to learn about |
See Also:
-
《Oracle Database SQL Language Reference》了解类型的约束的更多信息
SQL> INSERT INTO hr.employees (employee_id, last_name) values (999, 'Smith');
.
.
.
ERROR at line 1:
ORA-01400: cannot insert NULL into ("HR"."EMPLOYEES"."EMAIL")
只有在表不包含任何行或指定默认值的情况下,才能给列添加NOT NULL约束。
另见:
-
《Oracle Database 2 Day Developer's Guide》关于向表中添加 NOT NULL约束的示例
-
《Oracle Database SQL Language Reference》关于使用 NOT NULL 约束的限制
-
《Oracle Database Development Guide》了解何时使用 NOT NULL 约束
Unique 约束
Unique 约束要求在一个列或列集中的每个值是唯一的。 在一个表中, 不允许多个行在有唯一键约束的列(唯一键)或列集(复合唯一键) 上具有重复值。
-
客户电话号码, 其主键是客户号
-
部门名称, 其主键是部门编号
CREATE TABLE employees ( ...
, email VARCHAR2(25)
CONSTRAINT emp_email_nn NOT NULL ...
, CONSTRAINT emp_email_uk UNIQUE (email) ... );
如下例所示,emp_email_uk 约束可以确保没有任何两名雇员具有相同的电子邮件地址。
SQL> SELECT employee_id, last_name, email FROM employees WHERE email = 'PFAY';
EMPLOYEE_ID LAST_NAME 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 at line 1:
ORA-00001: unique constraint (HR.EMP_EMAIL_UK) violated
例 5-1 Unique 约束
SQL> SELECT employee_id, last_name, email FROM employees WHERE email = 'PFAY';
EMPLOYEE_ID LAST_NAME 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 at line 1:
ORA-00001: unique constraint (HR.EMP_EMAIL_UK) violated
另见
-
《Oracle Database 2 Day Developer's Guide》有关向表中添加 UNIQUE 约束的示例
Primary Key 约束
Primary Key 约束中的列或列集,其值能唯一地标识行。每个表只能有一个主键, 起到确定行的作用,并确保不存在任何重复的行。
Oracle 数据库实现的主键约束可以保证如下行为:
-
任何两行在指定的列或列集上都不具有重复值。
-
主键列不允许空值。
唯一键约束示例指示一个雇员 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 at line 1:
ORA-00001: unique constraint (HR.EMP_EMP_ID_PK) violated
SQL> INSERT INTO employees (last_name) VALUES ('Chan');
.
.
.
ERROR at line 1:
ORA-01400: cannot insert NULL into ("HR"."EMPLOYEES"."EMPLOYEE_ID")
- 有时,当您使用一个可延迟的约束选项来创建一个主键时,其生成的索引不是唯一的。
- 当你创建主键约束时,如果有一个现成的索引可用,则该主键约束会重用此索引,而不会隐式创建一个额外的新索引。
另见:
《Oracle Database 2 Day Developer's Guide》 和《Oracle Database Development Guide》了解如何向表中添加主键约束
Foreign Key 约束
下表列出了与参照完整性约束相关的术语。
表 5-2 参照完整性约束术语
| 术语 | 定义 |
|---|---|
| Foreign key |
约束定义中包含的列或列集, 它引用了参考键。 例如,employees 表中的 department_id 列是一个外键,它引用了 departments 表中的 department_id 列。
外键可以定义在多个列上。但是, 复合外键必须引用具有相同数量和相同数据类型列的复合主键或复合唯一键。
外键的值, 可以要么匹配被引用主键或唯一键的值, 要么为空。 如果一个复合外键的任何列为空,则该键的非空部分不一定要匹配父项中的任何相应部分。
|
| Referenced key |
被外键所引用的表中的唯一键或主键。 例如,相对于 employees 表中的 department_id 列来说,departments 表中的 department_id 列是被引用键。
|
| Dependent or child table | 包含外键的表。此表依赖于父表中被引用的唯一键或主键的值。 例如, employees 表是 departments 表的一个子表。 |
| Referenced or parent table |
由子表的外键引用的表。正是该表中的被引用值决定了在子表中特定的插入或更新是否可被允许。 Departments 表是 employees 表的父表。
|
Figure 5-1 Referential Integrity Constraints
Description of "Figure 5-1 Referential Integrity Constraints"
另见
《Oracle Database 2 Day Developer's Guide》 and 《Oracle Database Development Guide》了解如何向表中添加外键约束
自引用完整性约束
自引用完整性约束是一个引用同一个表中的父键为外键的约束
在下图中, 自引用约束可以确保在 employees.manager_id 列中的每个值对应于 employees.employee_id 列中的一个现有值。例如, 员工 102 的经理必须存在于 employees 表中。此约束消除了在 manager_id 列中存在错误的雇员人数的可能性。
空值和外键
父键修和外键
-
对删除或更新操作,不采取任何动作
在正常的情况下,如果结果会违反参照完整性, 用户不能修改被引用的键值。例如, 如果 employees.department_id 是对 departments 表的一个外键, 且有一些雇员属于某个特定部门,则试图删除该部门所在的行会违反约束。 -
级联删除
级联删除(DELETE CASCADE)即是当包含被引用键值的行被删除时,导致子表中的所有的外键依赖值所在行也会被删除。例如,删除departments 表中的某行, 会导致这个部门中的所有雇员行也被删除。 -
对删除置空
删除置空(DELETE SET NULL)即是当包含被引用键值的行被删除时,导致子表中的所有的外键依赖值被全部置空。 例如,删除部门表中的某行, 会导致将该部门中的所有雇员行的 department_id 列被置为空。
表 5-3 Update 和 Delete No Action 所允许的 DML 语句
| DML 语句 | 对父表发出 | 对子表发出 |
|---|---|---|
| | 若该父键值是唯一的,则总是可以 |
只有当外键值存在相应父键、 或部分为空、 或全为空时才可以
|
| |
只要该语句在子表中不留下任何这样的行——其外键值在父表中不存在,则允许
|
只要新的外键值仍可以引用父表中的某个被引用键值,则允许
|
| |
只要子表中没有行引用该父键值,则允许
| 总是可以 |
| | 总是可以 | 总是可以 |
| | 总是可以 | 总是可以 |
另见:
《Oracle Database SQL Language Reference》了解 ON DELETE 子句
索引和外键
- 可防止在子表上的全表锁定。相反,数据库只需要在索引上获取一个行锁。
- 消除在子表上进行全表扫描的需要。作为一个说明, 假定用户从部门表中删除部门 10 的记录。如果没有为 employees.department_id 建立索引, 则数据库必须扫描 employees 表, 以查看是否存在属于部门 10 的雇员。
另见:
-
"Locks and Foreign Keys"解释索引和未索引外键列的锁定行为
-
"Introduction to Indexes"说明指标的用途和特点
Check 约束
-
在 salary 列中不能有大于 10000 的值。
-
commission 列必须有一个值,但不能大于 salary。
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 at line 1:
ORA-02290: check constraint (HR.MAX_EMP_SAL) violated
另见:
《Oracle Database SQL Language Reference》了解检查约束的限制
完整性约束的状态
对现有数据和新数据的检查
您可以将约束设置为以下两种验证模式之一:
-
验证现有数据必须符合约束。例如,在employees.department_id上启用NOT NULL约束,并将其设置为VALIDATE检查现有的每一行是否具有部门ID。
-
不验证现有数据不需要符合约束。实际上,这是一种“相信我”的模式。例如,如果您确定加载到表中的每个sales都有一个日期,那么您可以在date列上创建一个NOT NULL约束,并将该约束设置为NOVALIDATE。非强制约束通常只对物化视图和查询重写有用。
对于NOVALIDATE模式下的约束,RELY 参数表明优化器可以使用该约束来确定连接信息。尽管约束不用于验证数据,但它支持对物化视图进行更复杂的查询重写,并支持数据仓库工具从数据字典检索约束信息。默认值为NORELY,这意味着优化器实际上不知道约束。
VALIDATE 和 NOVALIDATE 的行为都取决于约束是启用的还是禁用的。下表总结了这些选项组合。
Table 5-4 Checks on Modified and Existing Data
| Modified Data | Existing Data | Summary |
|---|---|---|
| | |
现有的和将来的数据都必须遵守约束。如果试图在一个已填充的表上应用一个新的约束,而现有的行违反该约束,则会导致一个错误。
|
| | | 数据库会检查该约束,但不需要对所有行为真。因此,现有的行可以违反该约束,但新的或修改后的行必须遵守该约束。这种模式通常用于包含已验证完整性的现有数据的数据仓库。 |
| | |
数据库禁用该约束、 删除其上的索引,并防止修改受约束的列。
|
| | | 不检查约束,也不需要为真。 |
另见:
当数据库检查约束的有效性时
下图描述了可延迟约束的选项。
不可延迟约束
例如, 假设您在 employees.last_name 列上创建一个不可延迟的 NULL 约束。如果用户试图插入一个没有姓氏的行, 则数据库会立即回滚该语句, 因为违反了 NOT NULL 约束。 在这种情况下, 将不会插入任何行。
可延迟约束
-
INITIALLY IMMEDIATE数据库在每个语句执行后,立即检查约束。如果违反了约束, 则数据库回滚该语句。 -
INITIALLY DEFERRED发出 COMMIT 时,数据库检查约束。如果违反了约束,则数据库回滚该事务。
另见:
《Oracle Database SQL Language Reference》关于约束属性及其默认值的信息
约束检查的示例
下面的例子可以帮助说明数据库何时执行检查约束。
假定如下:
-
employees表具有 "Self-Referential Integrity Constraints" 中所示的结构。
- 自引用约束要求在 manager_id 列中的条目依赖 employee_id 列的值。
例子:当父键值不存在时,往外键列中插入值
一些可能情况是:
- 如果 在 manager_id 列上没有定义 NOT NULL 约束,可以为第一行中的 manager_id 列输入一个空值。
因为在外键中允许使用空值, 此行可以被插入到表中。
- 如果该雇员是他或她自己的经理, 可以在 employee_id 和 manager_id 列中输入相同的值。这种情况表明数据库是在完全运行该语句之后,执行其约束检查的。若要允许输入在父键和外键中具有相同值的行,数据库必须首先运行该语句 (也就是插入新的行),然后再确定表中是否有任何行其 employee_id 对应新行的 manager_id 列。
- 一个多行的 INSERT 语句, 如一个具有嵌套 SELECT 语句的 INSERT 语句, 可以插入相互引用的多个行。例如, 第一行可能具有雇员 ID 200 和经理 ID 300,而第二行的雇员 ID 300 和经理 ID 200。约束检查被推迟到整个语句执行完成。首先, 所有行先被插入, 然后对所有行进行违反约束检查。
UPDATE employees SET employee_id = employee_id + 5000,
manager_id = manager_id + 5000;
本文深入探讨了数据完整性的概念,以及在数据库设计中确保数据准确性和一致性的多种技术。介绍了Oracle数据库中的各种完整性约束,包括NOT NULL、唯一、主键、外键、检查约束等,以及它们在保证数据质量方面的作用。

4475

被折叠的 条评论
为什么被折叠?



