Loading...
  所在位置:论坛首页 -> ┈┋电脑网络技术区┋┈ -> 数据库技术 -> DBA:实例讲解非"Unicode"数据库中的XML
标题:DBA:实例讲解非"Unicode"数据库中的XML收藏 编辑 删除 楼主 | 上一篇 下一篇
大胆的走
等级:社区游民
权限:普通用户
积分:5
金钱:113
声望:11
经验:11
发帖数:36
注册:2008年6月25日
资料 短消息2008-7-4 11:02:06
本文解释如何在 DB2® Viper 2 中的非 Unicode 数据库中使用 XML 本地数据类型。发现避免替换字符、查询结构指针和字符引用的使用的技巧,以及对产品附带的新的实用用户定义函数(UDF)的解释。

V9 中的 XML 支持

在 DB2 9 中,将 XML 数据包括在数据库中的惟一方法是让数据库使用 UTF-8 码集。即使 XML 数据中只包含 ASCII 代码点,这个限制也仍然有效。XML 是以二进制格式存储在数据库中的,文本节点被存储为 UTF-8 代码点。在 DB2 9 中,不会对 XML 文档进行转换,因此必须以一系列的 UTF-8 字节的形式将 XML 文档插入到数据库中。 DB2 Viper 2 版解除了这一限制,即所有使用 pureXML 特性的数据库必须以 Unicode 方式创建。本文解释非 Unicode 数据库中的 XML 支持,以及如何在保证数据完整性的情况下安全地插入和查询 XML 文档。

例子场景:在不必迁移至 Unicode 码集的情况下迁移至 pureXML

假设您是一个已有的非 Unicode DB2 数据库数据库管理员(DBA),该数据库包含一个用于编目图书馆中的书籍的表。目前,这个表具有以下形式:

清单 1. 示例关系模式

CREATE TABLE BOOKCAT
(TITLE           VARCHAR(400) NOT NULL,
AUTHOR_LAST      VARCHAR(50) NOT NULL,
AUTHOR_FIRST     VARCHAR(50),
PUBLISHER        VARCHAR(100),
DATE_PUB         DATE NOT NULL,
ISBN             BIGINT NOT NULL PRIMARY KEY,
KEYWORD1         CHAR(20),
KEYWORD2         CHAR(20),
FIRST_PARA       CLOB(2K));

然而,在非 Unicode 关系表中,对于表中的数据有一些限制。如果书的题目或文本包含当前数据库的字符集以外的字符会怎样?为什么要将这个表限制为每本书只有两个关键字?如果对于一个新的条目,关键字太大怎么办?如果一本书有一个未知的出版日期,或者有多个作者,怎么办?

这个模式有太多的限制,所以您决定探索如何以 XML 的形式存储数据,但是又不想将现有的数据库迁移至 UTF-8。 假设这个书籍数据库还要接纳来自世界各地的新书,这些书可能用中文、俄语和其他语言编写。您希望用英语和本地语言存储关于书的元数据(题目、作者、第一段)。每本书可以建模为一个 XML 文档,取决于这本书的语言,这个 XML 文档的编码方式可能不同。那么,如何在非 Unicode pureXML 数据库中插入、存储和查询这些文档呢?

非Unicode 数据库中对 XML 的新的支持

在 DB2 Viper 2 中,用户现在可以在任何新的或已有的数据库中,在新的或已有的表中创建 XML 列。现在不再要求使用 Unicode 码集。创建 XML 类型的列的格式是:

CREATE TABLE ( XML);

请注意 CREATE TABLE 的语法仍然和以前一样。

例子:

清单2. 创建 XML 类型的列

CREATE TABLE xmlbookcat (

ISBN BIGINT NOT NULL PRIMARY KEY,

BOOK XML

);

为了修改一个表以添加一个 XML 列,必须能够合法地添加一个列到表中(即表中不能有不带 DEFAULT 子句的 NOT NULL 列,并且表中行的大小必须能够容纳新的 XML 列,这个列长度为 80 字节)。 DB2 允许用户在 CREATE TABLE 或 ALTER TABLE 的列声明中使用 CCSID UNICODE 或 CCSID ASCII 子句,以创建以不同于数据库其他部分的字符码集标识符(CCSID)存储的表。在非 Unicode 数据库中,将 XML 列添加到具有 CCSID UNICODE 属性的表中是非法的。那样的表不存储在数据库代码页中,在这些表中 XML 不受支持。如果尝试这么做,会产生 a -873(代码页不匹配)错误。在非 Unicode 数据库中创建一个新的用于存储 XML 数据的表。

例子:

CREATE TABLE untable(a VARCHAR(20)) CCSID UNICODE;

ALTER TABLE untable ADD xmlcol XML;

这将产生错误 SQL0873N Objects encoded with different encoding schemes cannot be referenced in the same SQL statement。

XML数据的存储

这些 XML 文档必须具有类似于下面的结构:

清单3. XML 文档的结构

<book>
<title> . . . </title>
<native_title> . . . </native_title>
<author>
<firstname> . . . </firstname>
<lastname> . . . </lastname>
<native_name> . . . </native_name>
</author>
<publisher> . . . </publisher>
<date> . . . </date>
<keyword> . . . </keyword>
<first_para> . . . </first_para>
<native_first_para> . . . </native_first_para>
<ISBN> . . . </ISBN>
</book>

由于不同的书籍可能用不同的语言编写,因此需要在同一个 DB2 列中存储包含不同字符集的 XML 文档。下面的例子显示,为了提高可读性,元素的名称采用 ASCII,但是元素中的值可以由任何有效的 Unicode 代码点组成。下面是这些 XML 文档的示例数据:

清单4. XML 文档的示例数据

<book>
<title> A Tale of Two Cities </title>
<author>
<firstname> Charles </firstname>
<lastname> Dickens </lastname>
</author>
<publisher> Signet Classics </publisher>
<date> 7/1/1997 </date>
<keyword> London </keyword>
<keyword> Sydney Carton </keyword>
<keyword> Paris </keyword>
<first_para> It was the best of times, it was the worst of times </first_para>
<ISBN> 0451526562 </ISBN>
</book>

清单5. XML 文档的示例数据

<book>
<title> Romance of the Three Kingdoms </title>
<native_title> 三國演義 </native_title>
<author>
<firstname> Lou </firstname>
<lastname> Guanzhong </lastname>
<native_name> 羅貫中 </native_name>
</author>
<publisher> Tuttle Publishing </publisher>
<date> 8/1/2002 </date>
<keyword> China </keyword>
<keyword> Han Dynasty </keyword>
<keyword> Empire </keyword>
<keyword> History </keyword>
<keyword> 周末 </keyword>
<first_para> Here begins our tale: The empire, long divided, must unite;
long united, must divide </first_para>
<native_first_para> 天下大势, 分久必合 ,合久必分 :周末七国分争
,并入于秦 ;及秦灭之后, 汉分争, 又并入于汉 </native_first_para>
<ISBN> 0804834679 </ISBN>
</book>

清单 6. XML 文档的示例数据

<book>
<title> War and Peace </title>
<native_title> Война и мир </native_title>
<author>
<firstname> Leo </firstname>
<lastname> Tolstoy </lastname>
</author>
<publisher> Modern Library </publisher>
<date> 7/9/2002 </date>
<keyword> Russia </keyword>
<keyword> History </keyword>
<keyword> Paris </keyword>
<keyword> Napoleon </keyword>
<keyword> Война </keyword>
<first_para> Well, Prince, Genoa and Lucca are now no more than private estates 
of the Bonaparte family </first_para>
<native_first_para> Eh bien, mon prince. Gênes et Lucques ne sont plus que 
des apanages, des поместья, de la famille Buonaparte </native_first_para>
<ISBN> 0375760644 </ISBN>
</book>

在了解如何在非 Unicode 数据库安全地存储这些数据之前,需要研究解析和查询执行期间 XML 数据在 DB2 中的流程,包括从客户机到服务器,以及从服务器到 pureXML 特性。

XML数据流

DB2的XML组件在 Unicode 代码页范围内操作。因此,所有 XML 解析、串行化、查询执行和压缩都必须在 Unicode 代码页范围内进行。当数据库是 UTF-8 数据库时,可以在数据库代码页中操纵数据。如果数据库采用非 Unicode 代码页(例如 ISO-8859-1),那么所有 XML 文档、XQUERY 字符串和片段都必须经过代码页转换,从数据库代码页转换为 Unicode(特别地,转换为 UTF-8,代码页 1208)。

DB2 的 SQL 组件在数据库代码页中操作。所有 SQL 比较、SQL 查询执行以及 SQL 类型转换都必须在数据库代码页中进行。如果查询由一些 XML 活动和一些 SQL 活动组成,那么要在 “XML” 和 “SQL” 这两个 “世界” 之间进行转换,因为每个世界的数据必须在正确的代码页中。

将数据从数据库代码页转换为 UTF-8 代码页称为转换到 “XML 世界”,而将数据从 UTF-8 转换到数据库代码页则称为转换到 “SQL 世界”。这是服务器上 SQL/XML 或 XQUERY 执行期间惟一的两种代码页转换形式。

为了使非 Unicode 数据库支持 XML,建议使用一个 Unicode 客户机,因为客户机必须能够呈现输入 XML 和结果 XML 文档中的所有字符。如果哦客户机代码页和数据库代码页相同,那么客户机与服务器之间就不存在转换,于是客户机就可以呈现输入 XML 和结果 XML 文档中的所有字符。

客户机可以临时使用 DB2CODEPAGE 环境变量覆盖代码页。如果将这个变量设置为 1208 (UTF-8),那么 DB2 就可以像在代码页 1208 中那样与客户机通信。用这种方法覆盖客户机代码页可能导致不正确的输出或者不能打印的字符(例如将 Kanji 数据从一个 943 数据库发送到一个已经覆盖了 DB2CODEPAGE 的 ASCII 客户机)。在 Windows 中,可以在 Windows 控制面板中修改语言和环境设置,以覆盖客户机环境的代码页。

什么东西要经过转换?

每当从 DB2 客户机发生任何请求到 DB2 服务器,所有字符数据(例如查询或 CHAR 主机变量)都要从客户机代码页转换为数据库代码页。不管请求中是否牵涉到 XML,这个过程都会发生。

查询的 XML 部分被转换到 XML 世界,以便 XML 解析器进行解析以及 XML 值的输出(串行化)。结果则被转换到 SQL 世界。这两个转换都可能引入替换字符,即当目标代码页不能呈现代码点时插入到字符流中的替换代码点。替换字符会导致服务器上损失数据完整性。本文将寻找避免替换字符的方法。

SQL 查询的结果和串行化 XML 在传回客户机之前被转换到 SQL 世界。在这个时候,如果串行化 XML 包含数据库代码页中不能表示的字符,或者在客户机代码页中不能表示的字符,就可能引入替换字符。下面讨论避免这两种替换字符陷阱的方法。

插入XML 文档

至此,您已经知道了数据如何转换到正确的代码页,那么,这对于插入 XML 文档有何影响呢?为了将一个 XML 文档插入到表中,可以使用一个包含 XML 数据的字符串:

清单 7. 包含XML数据的字符串

INSERT INTO xmlbookcat VALUES 
(0451526562, XMLPARSE (DOCUMENT '<?xml version="1.0" encoding="ISO-8859-1" ?>
<book>
<title> A Tale of Two Cities </title>
<author>
<firstname> Charles </firstname>
<lastname> Dickens </lastname>
</author>
<publisher> Signet Classics </publisher>
<date> 7/1/1997 </date>
<keyword> London </keyword>
<keyword> Sydney Carton </keyword>
<keyword> Paris </keyword>
<first_para> It was the best of times, it was the worst of times </first_para>
<ISBN> 0451526562 </ISBN>
</book>'));

整个 INSERT 语句被从客户机代码页转换到数据库代码页,然后 XMLPARSE 文档字符串被转换到 UTF-8,以便解析为 XML。这些转换并没有引入替换字符,并且是可逆转的,所以在这个场景中可以安全地插入 XML 数据。插入用数据库代码页编码,并且只包括在数据库代码页中可以表示的字符的 XML 文档总是安全的。即使对于非 Unicode 数据库,这一点仍然适用。但是,如果想在同一个环境中插入中文 XML 文档,情况会怎样呢?

2008-7-4 11:02:06 顶部
大胆的走
等级:社区游民
权限:普通用户
积分:5
金钱:113
声望:11
经验:11
发帖数:36
注册:2008年6月25日
  资料   短消息编辑 删除 引用 第2楼
 

清单 8. 插入中文XML文档

INSERT INTO xmlbookcat VALUES

(0804834679, XMLPARSE (DOCUMENT '

三國演義

Lou

Guanzhong

羅貫中

Tuttle Publishing

8/1/2002

China

Han Dynasty

Empire

History

周末

Here begins our tale: The empire, long divided, must unite;

long united, must divide

天下大势, 分久必合 ,合久必分 :周末七国分争

,并入于秦 ;及秦灭之后, 汉分争, 又并入于汉

0804834679

'));

数据库服务器收到这条 SQL 语句并将其转换到数据库代码页时会遇到一个问题,中文字符都不能在数据库代码页(只由 ISO-8859-1 中的 ASCII 字符组成)中表示。这时,DB2 用一个替换字符来替代这些缺失的字符。取决于目标代码页,这个替换字符的代码点可能有所不同,但是对于代码页 819 (ISO-8859-1),它是十六进制字符 0x1A。在客户机上,这通常显示为一个问号('?')。

替换字符的存在意味着数据完整性已经丢失。原始文档的中文字符被丢失,通过将数据流发回客户机不能获得这些字符。在任何其他代码页中,替换字符总是被转换为另一个替换字符。

在以上 XML 文档中,XML 解析器将书的本地题目解析为 "????"(每个 ? 字符为 0x1A 代码点)。这时一个合法的 XML 字符流,解析器并不会抛出错误,但是 XML 列中的数据现在却被毁坏。选择这个文档只能导致替换字符串被传回客户机。

注意,如果数据库是以代码页 943 创建的,由于该代码页支持 Shift-JIS 编码代码点,上述 INSERT 语句是安全的,并且文档会被插入到数据库中。对于俄文的例子,只有将 XML 文档插入支持 ISO-8859-5 (Cyrillic) 编码的数据库才是安全的。

如果用户经常使用包含大量数据库代码页中不能表示的代码点的 XML 文档,那么最完美的解决方案是使用 Unicode 数据库。在服务器上,解析或串行化不会引入替换字符。注意,将数据返回到非 Unicode 数据库仍然可能引入替换字符。但是,数据库中的 XML 文档不受影响。

使用ENABLE_XMLCHAR参数

在Viper 2版中,有一个新的 DBA 配置参数,该参数可以防止在 CHAR 类型的主机变量或字符串上执行 XML 解析。这个参数作用范围为整个数据库,它的名称为 ENABLE_XMLCHAR。默认情况下,所有数据库创建时 ENABLE_XMLCHAR 被设置为 TRUE,但是也可以将它设置为 FALSE,以便当用户在 CHAR、VARCHAR 或 CLOB 类型且非 BIT DATA 类型的主机变量上执行进行隐式或显式 XMLPARSE 操作的查询时,抛出一个错误(-20429)。在这个场景中,CHAR FOR BIT DATA、VARCHAR FOR BIT DATA 和 BLOB 都被接受。 更改这个数据库参数值的命令是:

db2 update db cfg for database name using ENABLE_XMLCHAR FALSE

之后,DBA 需要再循环实例,以使更改生效。 例如,如何用户在一个 ISO-8859-1 代码页的数据库上执行:

INSERT INTO XTAB VALUES (XMLPARSE(DOCUMENT 'I just

joined the ΔΨΠ fraternity!' PRESERVE WHITESPACE));

那么产生的插入到 XTAB 中的 XML 文档包含 3 个替换字符,用于替代希腊字母。如果 ENABLE_XMLCHAR 被设为 FALSE,那么这条 INSERT 语句将产生一个错误:

SQL20429N The XML operation "XMLPARSE" is not allowed on strings

that are not FOR BIT DATA on this database.

这个错误告诉用户,对于 CHAR 类型 XMLPARSE 是不允许的。为了适当地将这个 XML 文档插入数据库,用户需要使用一个外部 API 将字符串 "I just joined the ΔΨΠ fraternity!" 绑定到一个 BLOB、CHAR FOR BIT DATA 或 VARCHAR FOR BIT DATA 主机变量。这样可以用与客户机代码页相同的代码点将文本插入 XML 列。

安全XML文档的插入

插入包含不能在数据库代码页中表示的代码点的文档的惟一安全的方法是使用一个 BLOB 或 BIT DATA 主机变量。这不能从 CLP 中完成。

将 XML 绑定到一个 BLOB 主机变量有助于避免替换字符,因为 DB2 不会在 BLOB 数据上执行代码页转换。为了确定 BLOB 中的数据的代码页,有一定的规则。

如果 BLOB XML 流包含一个 byte-order mark (BOM),那么可以用它来确定 BLOB 中的数据所设的代码页。这被称作 “内部编码”,因为主机变量上没有 “外部编码”(即没有与主机变量相关联的代码页)。

如果 BLOB XML 流不包含 BOM,那么解析这个流,以便在 XML 数据头部中的 "" 格式中发现 XML 属性 encoding。encoding 属性(如果存在)被映射为一个 CCSID,然后这个 CCSID 被用作内部编码。

如果没有 BOM,也没有给定的编码属性,则假定 BLOB 数据由 UTF-8 代码点组成。注意,在 XML 解析之前不会进行检查以确保 BLOB 只包含合法的 UTF-8 代码点。如果传入一个包含单独一个代码点 0xDB 的 BLOB 以将其解析为 XML,那么会导致一个错误,因为这不是合法的 UTF-8 代码点。对于 XML 可以包含的合法 UTF-8 代码点还有更多的限制。要了解关于这一方面的更多信息,请参阅 参考资料小节。

用于 “清理” XML 文档的UDF

最后一种办法是将有问题的字符转换为十六进制字符引用(格式为 "&#xhhhh;",其中 hhhh 是字符的十六进制 Unicode UTF16 代码点)。十进制字符引用可以在任何 XML 片段中使用,并且在 XML 解析期间被实际的代码点替换。字符串 "I just joined the ΔΨ΀ fraternity!" 在 UTF-8 中相当于 "I just joined the ΔΨΠ fraternity!" 。

为了帮助用户转换 XML 文档,DB2 提供了两个 UDF,这两个 UDF 在 XML 文档被插入数据库之前测试和清理 XML 文档。

第一个 UDF 名为 TEST_XML,它以一个包含 XML 文档的 BLOB 为参数(较可取的方法是使用一个 BLOB_FILE,该 BLOB_FILE 引用一个用 UTF-8 码集编码的 XML 文本文件),并输出一个 Boolean 值。当 TEST_XML 被调用时,DB2 尝试将这个 BLOB 从 UTF-8 转换为数据库代码页,如果在转换期间没有遇到替换字符则返回 TRUE,如果遇到替换字符则返回 FALSE。这个函数不会插入文档或者修改 BLOB。它只是一个测试,看是否可以安全地将这个 XML 文档作为 CHAR、VARCHAR 或 CLOB 插入到数据库中,而不会损失数据完整性。

如果该 BLOB 是以 UTF-8 之外的任何代码页编码的,那么用户可以为 TEST_XML 指定输入 BLOB 的代码页,作为该函数的第二个可选参数。

第二个 UDF 执行转换操作,进行换码符替换。这个 UDF 名为 CLEAN_XML,它以一个包含 XML 文档(假设使用 UTF-8 代码页)的 BLOB 或 BLOB 文件为输入,并输出一个 CLOB,该 CLOB 包含使用数据库代码页的 XML 文档,其中每个不能安全地转换到数据库代码页的代码点被替换为换码符形式 "&#xhhhh",其中 hhhh 是字符的十六进制 UTF16 代码点。这个函数不会插入文档或者修改 BLOB,它可以与 INSERT/XMLPARSE 结合使用,以便将任何给定的 BLOB XML 文档安全地插入到任何数据库中,例如:

INSERT INTO XTAB VALUES (XMLPARSE(CLEAN_XML(:BLOB_HV)));

如果该 BLOB 是以 UTF-8 之外的任何代码页编码的,那么用户可以指定输入 BLOB 的代码页作为 CLEAN_XML 可选的第二个参数。

例子:

假设以下 XML 文档是作为输入 BLOB 提供给使用代码页 ISO-8859-7 的数据库的一个样本。(注意,下面的 Sterling 中使用的符号 "é" 和 "í" 不属于 ISO-8859-7)。

示例输入:

<?xml version="1.0" encoding="utf-8" ?>
<a> Stérlíng
</a>

在上述输入上运行 TEST_XML 所产生的输出值为 1,因为 Sterling 中使用的 "é" 和 "í" 符号不属于 ISO-8859-7,因此需要被替换。

类似地,运行 CLEAN_XML 将导致 Stérlíng 被转换为 Stérlíng,单词 é 已经被对应的十六进制值 é 替换,而 í 则被对应的十六进制值 í 替换。

示例输出:

<?xml version="1.0" encoding = "utf-8" ?>
<a>
 Stérlíng 
</a>

现在,不管数据库使用何种代码页,都可以将这个 XML 插入到包含 XML 列的表中。对于这两个 UDF,需要提醒的是,它们并不会将 BOLB 解析为 XML 文档。这两个函数中不会检查格式良好性,因此可能存在这样的情况,即元素标记名称或 XML 文档中其他非文本部分被更改为十六进制字符引用。如果 XML 文档中的元素标记名称或 XML 头部信息中包含不能在数据库代码页中呈现的代码点,那么从 CLEAN_XML 得到的文档不再是一个格式良好的 XML 文档。

非Unicode数据库中XML数据的查询

XML 串行化是将 Unicode XML 值绑定到主机变量,以便最终传回客户机。如果值被串行化为不是 FOR BIT DATA 的 CHAR、VARCHAR 或 CLOB 类型,那么会有一个到 SQL 世界的代码页转换。BLOB 串行化不会经过任何代码页转换,但是在串行化后得到的头部中会被赋予一个 "encoding" 属性,该属性默认为 UTF-8 编码。

如果代码点不能在数据库代码页中呈现,则可能引入替换字符。如果引入了这些字符,那么用户会收到一个错误 -20412,这与 XML 白皮书中的一条规则相符,即 XML 值的串行化不能引入替换字符。

现在看看这个场景中 SELECT 如何工作。

语法:

SELECT XMLSERIALIZE(

2008-7-4 11:02:40 顶部
第1页 共1页 共1个回复     <<    >>    
 快速回复
  • 支持UBB,HTML标签

  • 高级回复

  • 操作选项:评分 加精 解精 奖惩 设专题 设公告 解公告 固顶 总固顶 解固顶 结帖 解结帖 锁帖 解锁 移帖 删帖
      首页 | 购买指南 | 商业版本 | 虚拟主机 | 特色介绍 | 下载中心 | 支付方式
    Copyright 2004-2008 BBSGood.com Powered By: BBSGood.Speed Version 5.0
      咨询电话:0575-85513832、0575-85513825(传真)、7*24小时咨询服务:13606552007 不良信息举报中心 浙ICP备05029817号
      业务QQ:38958768、客服QQ1:415896239、客服QQ2:343896043、MSN:jccsxx@hotmail.com