简介
因为数据完整性对于许多业务应用程序至关重要,所以在计算机体系结构的所有层次上都设计了保证完整性的特殊技术:从微程序设计层到操作系统和数据库管理系统层,甚至到应用程序层都是这样。本文通过利用 DB2 对数据类型提供的支持讨论数据库级别上的数据完整性,还讨论了如何使用 XML 模式标准(XML Schema Standard)[ W3C2001] 将 DB2 数据完整性约束映射至 XML。这一映射很有趣,因为可以在解析 XML 文件期间应用 DB2 中所定义的完整性规则;即在 DB2 中加入数据之前应用完整性规则。我在本文中介绍的 SQL 命令是一个命令子集的一部分,这个命令子集常用于以下 DB2 平台:UNIX®、Windows®、AIX®、iSeries™、z/OS™ 和 OS/390® [IBM2002]。所提出的映射与工作草案“SQL/XML mapping” [ISO2002]一致,ANSI-NCITS H2 和 ISO/IEC-SC32 委员会正在讨论该草案。
假设客户要求您开发一个新的记帐系统。您要请他在以下开发选项中选择一个:
选项 1 — 开发的新系统具有所要求功能的 80%,并且 100% 保证数据的完整性。
选项 2— 开发的新系统具有所要求功能的 100%,但只保证 99% 的数据完整性。
您的客户很可能选择 选项 1,这是因为某些系统中需要百分之百的数据可靠性。在这种情况下,宁可不实现某些功能,也不允许可能存在不可靠的数据。以牺牲系统的性能为代价要比牺牲数据可靠性更好。
按照 C.J. Date [Date2000]所说,数据完整性的约束就是“业务规则”的另一种叫法。他拥护这样的主张,用声明的方法定义业务规则比用过程化方法更好。Date 尤其强调声明性方法的以下优点:
“我们可以避免重复地强制执行规则。我们不必在大量不同的过程化代码块或众多不同的例程(“方法”,或任何名称)中嵌入“相同”的强制执行代码。我们只需对规则声明一次,就可以让系统进行工作。”
在避免了冗余的同时,还可以避免不一致的问题;即,某一规则的“副本”之一可能还没有随着业务规则的更改而得到更新。按照 Date 所说,另一个优点是只需在数据库目录这一处记录规则,并且可以在那里查询规则。
DB2 和 XML 模式标准使用声明形式来定义完整性约束。本文中所用的完整性约束的分类改编自 Date 在 [Date2001]中提议的分类。
本文的结构安排如下:
首先介绍 DB2 支持的数据类型,其中用简单的示例研究了数据完整性。
接下来,介绍 XML 模式标准的一些基本概念。
本文的主要部分演示了如何将 DB2 中定义的 完整性规则映射至 XML 模式标准。
最后,我介绍了所提出映射的一些 限制。
DB2 中的数据类型
按照 Date [Date2000b]所说:
“类型是一组指定的值(也就是,所讨论类型的所有可能的值),以及一组相关联的运算符,这些运算符可应用于所讨论类型的值和变量。”
DB2 有表示数字、日期、时间和字符串等的预定义数据类型。例如,DB2 INTEGER 类型指的是在 -2147483648 和 +2147483647 之间的整型数值的集合,并且包含各种相关的运算(加、减和乘等)。根据定义, INTEGER 类型的列可以取 NULL 值,NULL 是可用于所有数据类型的特殊值,记住这些很重要。 NULL 值用于各种情形,例如,表示未通知值或未知值时,或表示不适用于某一情形的值。作为主键一部分的列不能取 NULL 值。定义类型值的域的规则称为“类型约束”。
图 1 概括了主要的 DB2 内置数据类型。
图 1. DB2 内置数据类型
创建表时,每一列都与某一数据类型关联。在下面的示例中观察 DEPARTMENT 和 PROJECT 表的创建:
CREATE TABLE DEPARTMENT
( DEPTNO INTEGER NOT NULL,
DEPTNAME CHAR(50),
PRIMARY KEY (DEPTNO)
);
CREATE TABLE PROJECT
( PROJNO INTEGER NOT NULL,
PROJNAME CHAR(50) NOT NULL,
DEPTNO SMALLINT NOT NULL,
START_DATE DATE NOT NULL,
END_DATE DATE,
PRIMARY KEY (PROJNO),
FOREIGN KEY (DEPTNO) REFERENCES
DEPARTMENT ON DELETE RESTRICT);
DB2 保证只有有效的日期才能归入 START_DATE 列。这一完整性规则称为“属性约束”。
因为表的主键是 PROJNO 列,所以禁止使用 NULL 值或重复值。只有这样,我们才能标识表的每一行。我们把表中主键的定义归类为“实体约束”类型的约束。
还要注意有对外键( DEPTNO )的定义。这意味着 DB2 将只允许 DEPARTMENT 表中存在的值。我们可以把这一约束归类为“引用完整性约束”。Date 把这定义为“数据库约束”的特殊情况。
即使有了所有这些完整性类型,仍然不能防止包含带无效业务数据的行,如下所示:
INSERT INTO PROJECT
(PROJNO,
PNAME,
DEPTNO,
START_DATE,
END_DATE)
VALUES
( -5, -- NEGATIVE VALUE
"PR1",
5,
"13-DEC-1400", -- IRREAL DATE
"01-JAN-1100"); -- END_DATE < START_DATE
因为 PROJNO 和 DEPTNO 列的数据类型是兼容的( INTEGER 和 SMALLINT ),所以系统不能防止以下查询的执行:
SELECT PROJNO + DEPTNO AS SIC
FROM PROJECT
有了这一定义后, PROJECT 表接受对于业务无效的值并且允许无意义的运算:将项目代号与部门代号相加毫无意义。
要解决这样的问题,可定义新的业务规则:
(a) 项目代号是 1 和 50000 之间(包括 1 和 50000)的整数,部门代号是 1 和 400 之间(包括 1 和 400)的整数。
(b) 起始日期必须大于“01-JAN-2000”。
(c) 终止日期必须大于起始日期。
(d) 禁止不兼容列之间的运算。
本来可以在应用程序级别验证这些规则。然而,由于已陈述过的原因,我们倾向于在数据库级别上保证这些规则。为此,我们将使用 UDT(用户定义的数据类型,user-defined data type)和检查约束。
清单 1显示了符合新规则的对象的新定义。
通过创建 UDT DEPTNO 和 PROJNO ( 清单 1— 01-05 行)和定义新表中的检查约束( 清单 1 — 12-13 行及 20-21 行),实现业务规则 (a) 和 (d)。注:不必在 NEW_PROJECT 表的 DEPTNO 列中创建检查约束,因为在该列中已经有引用完整性的定义。
倘若 DB2 实现了在 SQL ANSI(高级)标准中有所预见的 CREATE DOMAIN 语句,则只需创建新的域,而无需在表中定义检查约束。
通过定义 NEW_PROJECT 表中的检查约束( 清单 1 — 24-28 行),实现业务规则 (b) 和 (c)。还创建了单值类型 NAME ,以便在需要 50 个字符名称的地方使用。请查阅以下 UDT 和 NEW_DEPARTMENT 及 NEW PROJECT 表的定义。
清单 1— 新表和单值类型的创建
01. CREATE DISTINCT TYPE DEPTNO AS INTEGER
02. WITH COMPARISONS;
03.
04. CREATE DISTINCT TYPE PROJNO AS INTEGER
05. WITH COMPARISONS;
06.
07. CREATE DISTINCT TYPE NAME AS CHAR(50)
08. WITH COMPARISONS;
09.
10. CREATE TABLE NEW_DEPARTMENT
11. ( DEPTNO DEPTNO NOT NULL
12. CHECK (DEPTNO >= DEPTNO(1)
13. AND DEPTNO <= DEPTNO(400)),
14. DEPTNAME NAME,