在 DB2 中使用 XML 模式和数据类型
2009-11-12 00:00:00 来源:WEB开发网简介
因为数据完整性对于许多业务应用程序至关重要,所以在计算机体系结构的所有层次上都设计了保证完整性的特殊技术:从微程序设计层到操作系统和数据库管理系统层,甚至到应用程序层都是这样。本文通过利用 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,
15. PRIMARY KEY (DEPTNO)
16. );
17.
18. CREATE TABLE NEW_PROJECT
19. ( PROJNO PROJNO NOT NULL
20. CHECK (PROJNO >= PROJNO(1)
21. AND PROJNO <= PROJNO(50000)),
22. PROJNAME NAME NOT NULL,
23. DEPTNO DEPTNO NOT NULL,
24. START_DATE DATE NOT NULL
25. CHECK (START_DATE >= DATE('01-01-2000')),
26. END_DATE DATE,
27. CHECK (END_DATE > START_DATE),
28. PRIMARY KEY (PROJNO),
29. FOREIGN KEY (DEPTNO) REFERENCES
30. NEW_DEPARTMENT ON DELETE RESTRICT
31. );
这些新规则的定义有助于保证更佳的数据完整性。因为以声明形式来定义,所以 DB2 保证这些规则始终会得到遵守。
接下来,我们可以看看如何将这些规则映射至 XML 域。为简单起见,我们将只使用与 NEW_PROJECT 表有关的类型和规则。
XML 模式 — 基本概念
随着 XML 越来越多地用于以数据为中心的应用程序,对数据验证的需要导致使用强类型。从 1998 年 1 月起,W3C 模式工作组(W3C Schema Working Group)已经收到多个模式语言建议(XML-DATA、XDR、SOX、DDML 和 DCD),最后产生了 XML 模式标准(于 2001 年 5 月发布)。XML 模式不是现有的唯一模式语言。其它模式语言有:RELAX、Schematron 和 Examplotron 等。
XML 模式语言与 DTD(文档类型定义,Document Type Definition)相比有许多优点。在这些优点中我们要强调的是:
更丰富的数据类型组
可以定义新的数据类型
允许使用多个元素/属性来定义唯一性
在模式定义中使用 XML 格式
允许使用名称空间(允许不同语法的组合,从而避免名称冲突)
XML 模式标准第 2 部分 [W3C2001]定义了内置数据类型。 图 2介绍 XML 模式标准的预定义类型(基本类型和仅按约束派生的类型)。
图 2. XML 模式预定义数据类型
从预定义数据类型(基本类型或派生类型)派生其它数据类型是有可能的。我们将使用按照约束的派生来定义与 DB2 提供的数据类型对应的数据类型。
可以将一种 XML 模式标准分类为:
简单类型— 定义与出现在属性或只包含文本(不能包含属性)的元素中的文本有关的约束。
复杂类型— 定义包含属性或其它嵌套元素的元素的约束。复杂类型可以是匿名的(在要用到复杂类型的元素内定义它时),也可以是命名的(当可以预见将在其它元素的定义中使用它时 — 必须在使用它的元素之外定义它)。
我们必须为每个元素或属性选择一种类型。在下面的示例中,我们定义了一个名为 PROJECTS 的元素。该元素与一个使用序列构成器(sequence composer)(指明元素必须以声明它们的顺序出现)的匿名复杂类型相关联。 PROJECT 元素与一个命名的类型相关联。注:属性 minOccurs 和 maxOccurs 定义元素基数(使用 DTD 时,基数定义十分有限)。这样就决定了 PROJECTS 元素中可以出现 0 个或多个嵌套的 PROJECT 元素。
<xsd:element name="PROJECTS">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="PROJECT" type="PROJECT"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
将 DB2 数据类型映射至 XML 模式
XML 模式的重要特征是可以从其它已存在的模式创建新的模式。首先,使用 XML 模式标准的预定义数据类型,创建 DB2 数据类型的映射(模式 db2xml.xsd )。然后,使用 db2xml.xsd ,用与业务相关的数据类型创建新的模式(模式 myTypes.xsd )。最后,创建一个验证基本 db2xml 模式和 myTypes 模式所含数据的模式(模式 new-project.xsd )。
要验证使用 XML 模式的 XML 文档,有必要使用接受这种验证的解析器,如 xerces 和 xsv 等。解析器接收 XML 文件作为输入(要验证的数据)并接收带模式的文件(用于验证的语法),然后生成结果,报告结构和内容是否遵循语法中定义的规则。
图 3概括了为得到验证器模式而创建的模式。图的下部表示解析器程序,它读取两个输入文件并生成验证结果。
图 3. 模式层和解析器
附录中的 表 1 介绍 DB2 类型和 XML 模式之间的第一个映射级别(DB2xml)。这一映射级别不依赖业务对象。我们可以在 DB2xml.xsd 文件中详细查看如何实现 CHAR 类型的映射。
清单 2. DB2 CHAR 类型至 XML Schema xsd:string 类型的映射
01. <xsd:simpleType name="CHAR">
02. <xsd:annotation>
03. <xsd:appinfo>
04. <db2xml:sqltype kind="PREDEFINED"
05. name="CHAR" maxLength="254"/>
06. </xsd:appinfo>
07. </xsd:annotation>
08. <xsd:restriction base="xsd:string">
09. <xsd:maxLength value="254"/>
10. </xsd:restriction>
11. </xsd:simpleType>
为映射 CHAR 所选的类型是 xsd:string ,限制它最多有 254 个字符( 清单 2— 09 行)。大小约束是用面(facet)定义的,这是派生自基本数据类型的属性。
XML 模式语言允许为模式定义两种注释:文档元素(以便包括注释)和 appinfo 元素(供其它应用程序使用的信息)。在这个映射中,我们对 appinfo 编码以说明映射的是 DB2 中的哪个原始类型。
要映射 DATE 、 TIME 和 TIMESTAMP 类型,有必要使用定义各类型格式的模式面(pattern facet)。例如,正则表达式 "\p{Nd}{4}-\p{Nd}{2}-\p{Nd}{2}" 定义日期的格式。
接下来是 DB2 单值类型(UDT)的定义。创建 PROJNO 、 DEPTNO 和 NAME 类型。我们可以在 myTypes.xsd 文件中详细查看如何定义 PROJNO 类型,如 清单 3所示。
清单 3. PROJNO 单值类型的映射。
01. <xsd:simpleType name="PROJNO">
02. <xsd:annotation>
03. <xsd:appinfo>
04. <db2xml:sqltype kind="DISTINCT" typeName="PROJNO"/>
05. </xsd:appinfo>
06. </xsd:annotation>
07. <xsd:restriction base="db2xml:INTEGER">
08. <xsd:minInclusive value="1"/>
09. <xsd:maxInclusive value="50000"/>
10. </xsd:restriction>
11. </xsd:simpleType>
注意单值类型 PROJNO 从类型 db2xml:INTEGER 映射而来(清单 3 - 第 7 行),将值限制在 1 到 50000 之间(包括 1 和 50000)。除了单值类型之外,我们还在该模式中定义 START_DATE ,它实现对项目起始日期的检查约束的映射( <xsd:minInclusive value="2001-01-01"/> )。
最后,我们可以定义验证数据的模式。为此,我们将使用前面定义的 db2xml.xsd 和 myTypes.xsd 。 清单 4演示了验证器模式。
清单 4. 验证器模式。
01. <?xml version="1.0" encoding="ISO-8859-1"?>
02. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
03. xmlns:db2xml="http://www.acme.org/DB2xml"
04. xmlns:mT="http://www.acme.org/myTypes">
05. <xs:import namespace="http://www.acme.org/myTypes"
06. schemaLocation="myTypes.xsd"/>
07. <xs:import namespace="http://www.acme.org/DB2xml"
08. schemaLocation="DB2xml.xsd"/>
09. <xs:complexType name="PROJECT">
10. <xs:sequence>
11. <xs:element name="PROJNO" type="mT:PROJNO" minOccurs="1"/>
12. <xs:element name="PROJNAME" type="mT:NAME" minOccurs="1"/>
13. <xs:element name="DEPTNO" type="mT:DEPTNO" minOccurs="1"/>
14. <xs:element name="START_DATE" type="mT:START_DATE"
15. minOccurs="0"/>
16. <xs:element name="END_DATE" type="db2xml:DATE"
17. nillable="true"/>
18. </xs:sequence>
19. </xs:complexType>
20.
21. <xs:element name="PROJECTS">
22. <xs:complexType>
23. <xs:sequence>
24. <xs:element name="PROJECT" type="PROJECT"
25. minOccurs="0" maxOccurs="unbounded"/>
26. </xs:sequence>
27. </xs:complexType>
28. <xs:unique name="PROJNO_primary_key">
29. <xs:selector xpath="PROJECT"/>
30. <xs:field xpath="PROJNO"/>
31. </xs:unique>
32. </xs:element>
33. </xs:schema>
第 2-8 行定义并导入了基本模式。第 9-19 行定义了名为 PROJECT 的复杂类型。该类型由对应于示例表各列的元素组成。元素 END_DATE 是根据来自 db2xml 模式的类型定义的。请注意指明元素可以取 NULL 值的 nillable="true" 属性。第 28 到 31 行定义约束唯一性(等同于主键)。
在 XML 模式标准(第 1 部分) [W3C2001b]的“Section 1.1 Purpose”中,提出了关于语言用途方面的警告:
“有些应用程序所要求的约束能力可能无法用该语言表达,因此需要执行它们自己额外的验证。”
在 NEW_PROJECT 表中,有一种检查约束(“ END_DATE > START_DATE ”)不能只用 XML 模式资源来验证。为了解决这样的问题,最好的替代方案是使用基于 Schematron 规则的语言。除了 图 3中已显示的常规验证以外,还将执行另一个 Schematron 规则的验证。利用 annotation/appinfo 元素实现 XML 模式与 Schematron 之间的链接。
在下面的 清单 5 中,我们可以看到使用了 schematron 符号的完整性规则重新定义 END_DATE 元素。
清单 5. 检查约束( END_DATE > START_DATE )。
01. <xsd:element name="END_DATE" type="db2xml:DATE" nillable="true">
02. <xsd:annotation>
03. <xsd:appinfo>
04. <sch:pattern name="Check constraint end_date > start_date">
05. <sch:rule context="END_DATE">
06. <sch:assert test="( ./@xsi:nil='true' or
07. (number(translate(./text(),'-','')) >
08. number(translate(../START_DATE/text(),'-',''))))">
09. END_DATE must be greater than START_DATE.
10. </sch:assert>
11. </sch:rule>
12. </sch:pattern>
13. </xsd:appinfo>
14. </xsd:annotation>
15. </xsd:element>
通过在所选上下文(END_DATE — 第 5 行)中使用 XPATH 表达式(第 6-8 行)呈现业务规则。XPATH 是构成 XML 标准系列的 W3C 标准。它旨在定义选择 XML 文档分段的路径。
要验证 schematron 规则,有必要使用 XSLT 处理器(比如 saxon)。XSLT 是另一个 W3C 标准,目的是在 XML 文件之间执行转换。
XSLT 处理器接收 XML 和 XSL 文件(转换规则)作为输入,然后根据已建立的转换规则产生输出。
下面的列表显示了使用 saxon 验证 Schematron 规则的必需步骤。
saxon new_project.sch XSD2SCHTRN.XSL > proj_sch.xsd
saxon proj_sch.xsd schematron-basic.xsl > validator.xsl
saxon project.xml validator.xsl > result2.txt
第 1 步是将 schematron 规则从原文件抽取到临时文件的预处理过程。将该文件输入至另一转换( 第 2 步),由该转换生成 Schematron 规则的验证文件。在 第 3 步中,最终完成验证规则,生成结果。
必须要考虑的最终结果是结合了 XML 模式解析器的验证结果和验证 Schematron 规则的 XSLT 处理器的结果。 图 4概括了验证过程,考虑了 XML 解析器和 XSLT 处理器。
图 4. 使用 XML 模式 + Schematron 的 XML 验证
限制
所提出的映射有如下一些限制:
本文没有处理从 XML 模式到 DB2 的映射,而只处理了从 DB2 到 XML 模式的映射。
没有映射 UDT“结构化类型”和“引用类型”,因为它们在大型机平台中不出现。本文只打算介绍所有平台共有的类型。
倘若数据在另一个 XML 文档中,XML 模式标准不会考虑进行引用完整性检查。可以用 Schematron 绕开这一限制,正如检查约束中所做的那样。
没有讨论一些重要的映射特征(如字符集、整理顺序和标识符)。
没有映射存在于所有 DB2 平台的 GRAPHIC、VARGRAPHIC 和 LONG VARGRAPHIC 类型。
我们在前面提到冗余会造成不一致的问题。在将 DB2 完整性规则映射至 XML 模式时,有一些应加以控制的冗余。一个替代方案是创建一个从 DB2 目录自动抽取完整性规则的程序,从而创建自动映射。
结束语
必须不断改进数据完整性。DB2 有几个保证完整性的特性。在本文中,我们知道了如何使用 XML 模式标准将 DB2 完整性约束映射至 XML 域。这种验证可以远程进行,无需连接到 DB2。主要的好处是,当把数据插入 DB2 时,数据已经得到验证。可以用几种方法实现在 DB2 中装入经验证的数据。一种选择是进行新的 XSLT 转换,创建用于装入的文件。另一种选择是使用 DB2 开发者园地的文章“将 <emphasis>XML Data<emphasis> 映射到 DB2 的另一种方法” [Lima2002]中介绍的数据登台方法。
在文章的最后部分,我们看到 XML 模式不能映射我们示例中定义的所有完整性规则。为了能够映射所有的规则,有必要将某种基于规则(Schematron)的模式语言与 XML 模式以及 XSLT 和 XPATH 标准的资源结合起来使用。
DB2 UDB Beta 测试版 V8.1 最近宣布它有一个 UDF(XML Extender)实现了用 XML 模式进行验证。
附录
表 1. 将内置 DB2 数据类型映射至 XML 模式
DB2 | XML 模式 | 映射 |
CHAR | xsd:string | <xsd:simpleType name="CHAR"> |
VARCHAR | xsd:string | <xsd:simpleType name="VARCHAR"> |
CLOB | xsd:string | <xsd:simpleType name="CLOB"> |
BLOB | xsd:hexBinary | <xsd:simpleType name="BLOB"> |
SMALLINT | xsd:short | <xsd:simpleType name="SMALLINT"> |
INTEGER | xsd:int | <xsd:simpleType name="INTEGER"> |
DECIMAL | xsd:decimal | <xsd:simpleType name="DECIMAL"> |
NUMERIC | xsd:decimal | <xsd:simpleType name="NUMERIC"> |
REAL | xsd:float | <xsd:simpleType name="REAL"> |
DOUBLE | xsd:double | <xsd:simpleType name="DOUBLE"> |
DATE | xsd:date | <xsd:simpleType name="DATE"> |
TIME | xsd:time | <xsd:simpleType name="TIME"> |
TIMESTAMP | xsd:dateTime | <xsd:simpleType name="TIMESTAMP"> |
更多精彩
赞助商链接