IBM® DB2® 9.5 for Linux, Unix and Windows 中最重要的新特性之一是 XML 更新功能。前一个版本 DB2 9 引入了 pureXML™ 支持,可以用 SQL/XML 和 XQuery 语言存储和查询 XML 数据,以及为 XML 数据编制索引。在过去,需要在数据库服务器之外修改 XML 文档,然后在 DB2 中执行全文档更新。现在,9.5 引入了 XQuery Update Facility。这是 XQuery 的一种标准化扩展,允许在 XML 文档中修改、插入或删除元素和属性。这会显著简化 XML 数据的更新,提供更好的性能。本文介绍新的 XML 更新功能,提供典型 XML 更新操作的示例,讨论如何避免常见的问题。
简介和背景知识
DB2 9 pureXML 在对 XML 的数据库管理支持方面处于行业领先地位。实际上,当 IBM 开发 pureXML 技术时,World Wide Web Consortium (W3C)(定义和维护 XML 和 XQuery 标准的组织)才刚开始定义 XML 的标准更新机制。由于缺少 XML 更新标准,DB2 9 通过 SQL UPDATE 语句支持全文档替换,例如:
create table xmlcustomer (cid bigint, info XML);
update xmlcustomer
set info = 'John Doe...'
where cid = 1783;
|
UPDATE 语句的 set 子句用一个新文档替换给定行中的现有文档。在这里,以字面值形式提供文档,但是也可以通过参数标志或主机变量把文档传递给数据库。在 DB2 9 中,需要修改 XML 文档中的元素或属性的应用程序常常在应用程序内部进行修改。这通常需要执行 图 1 所示的一系列处理。
- 应用程序提交一个 SQL 或 XQuery 语句以读取 XML 文档。
- 从数据库获得文档并把它序列化为文本格式。
- 把文档传输给客户机。
- 应用程序解析 XML 文档,通常使用文档对象模型 (DOM)。
- 应用程序使用 DOM API 修改文档。
- 在客户机应用程序中重新序列化文档。
- 应用程序提交一个 SQL UPDATE 语句,把更新后的文档传输给数据库服务器。
- 数据库服务器解析更新后的 XML 文档并替换旧文档。
图 1. 在 DB2 9 中更新 XML 文档
获取、解析和修改 XML 文档的过程也可以封装在数据库服务器上运行的存储过程中。处理步骤与 图 1 相同,只是不需要把文档发送给客户机并发送回来。
DB2 9.5 中新的更新功能把这个过程简化为一个步骤。应用程序只需向 DB2 服务器发送一个包含 XQuery 转换 表达式的 SQL UPDATE 语句(图 2)。这个表达式是即将形成的 XML 数据更新 XQuery 标准的一部分。
图 2. 在 DB2 9.5 中更新 XML 文档
在 DB2 9.5 执行这个 UPDATE 语句时,它找到相关的文档???修改指定的元素或属性。这在 DB2 存储层中执行,文档一直保持 DB2 内部的层次化 XML 格式,不需要任何解析或序列化。并发控制和日志记录在完整文档级进行。这种新的更新过程通常总体上比 图 1 所示的过程快 2 到 4 倍。
新的更新功能允许在 XML 文档中执行节点级修改。在大多数情况下,修改的节点是元素或属性。可以:
- 替换节点的值
- 用新的节点替换一个节点
- 插入一个新节点(甚至是在特定的位置,比如在给定的节点之前或之后)
- 删除节点
- 改变节点名称
- 在一个 UPDATE 语句中修改文档中的多个节点
- 在一个 UPDATE 语句中更新多个文档
本文的其余部分讨论如何用 XQuery 转换表达式执行这些 XML 更新。您将学习如何通过在 UPDATE 语句中嵌入转换永久地修改磁盘上的数据,以及在查询中读取 XML 数据的过程中 “临时地” 修改数据(而不是永久地修改数据)。如果应用程序需要接收的 XML 格式与数据库中使用的格式不同,后一种方法会很有帮助。
示例数据
为了解释新的 DB2 9.5 XML 更新功能,我们使用下面的 CREATE TABLE 和 INSERT 语句定义示例表和数据。xmlcustomer 表有两列,类型分别为 bigint 和 XML。表中包含两行数据,分别由 cid 值 1000 和 1001 标识。可以通过下载 下载 部分中的文本文件获得本文使用的示例数据和所有更新。
create table xmlcustomer (cid bigint, info XML); |
|
insert into xmlcustomer values (1000, '
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136'); |
|
insert into xmlcustomer values (1001, '
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333'); |
|
示例文档的层次结构见 图 3。在设计 XML 查询或更新时,记住这个树结构会有帮助,因为这些操作最终总是要在树中移动。
图 3. 示例文档的树结构
![示例文档的树结构](https://i-blog.csdnimg.cn/blog_migrate/40b9f8d72f8382dec1e49126bd1d9a6d.png)
更新 XML 数据 —— 一个简单的示例
更新 1 是一个简单的 XML 更新示例。它修改某一客户的电话号码。 SQL UPDATE 语句的总体结构仍然是大家熟悉的 “update… set…. where…” 语法。还可以看到 “set” 子句给 XML 列 “info” 设置一个新值,这个新值是一个 XMLQUERY 函数的结果。
XMLQUERY 函数使用 SQL/XML 查询 XML 数据。在 SQL 查询的 SELECT 子句中,常常使用 XMLQUERY 根据结果集的给定行中的 XML 文档提取或构造 XML 值。为了实现这个目标,XMLQUERY 函数允许在 SQL 语句中嵌入 XQuery 表达式。在这个示例中,使用 XMLQUERY 函数修改给定的 XML 文档。更新 1 中的 XQuery 表达式是一个转换表达式。转换表达式是 XQuery Update Facility 的核心,是即将形成的用于修改 XML 文档的 XQuery 扩展标准。
转换表达式的开头是可选的 “transform” 关键字,然后是 COPY、MODIFY 和 RETURN 子句。转换表达式的基本原理是,COPY 子句把来自 XML 列 (info) 的输入文档赋值给一个变量(在这里是 $new),然后 MODIFY 子句对此变量应用一个或多个更新操作,最后 RETURN 子句产生转换表达式的结果。
更新 1
update xmlcustomer
set info = xmlquery( 'transform. copy $new := $INFO
modify do replace value of $new/customerinfo/phone with "905-477-9011"
return $new')
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlberta90111905-477-9011 |
|
MODIFY 子句是特别让人感兴趣的部分,因为它定义实际执行的更新操作。看一下本文中的各个 MODIFY 示例。在这里,仅仅把 “phone” 元素的值替换为一个新的值 (905-477-9011)。在 更新 1 和其他许多典型场景中,RETURN 子句仅仅返回包含修改后的文档的变量。但是,也可以返回更复杂的表达式,包括元素构造和 FLWOR 表达式。同样,COPY 子句的右边常常只是包含输入文档的变量(在这里是 $INFO),但是也可以更复杂。COPY 子句的右边的运算结果必须是单一节点,也就是说,它不能是空的序列或包含多个节点的序列。这个节点可以有后代节点,这意味着它可以是整个 XML 文档的根(这种情况很常见)。
因为 “transform” 关键字是可选的,所以后面的示例忽略它。
在 SQL 查询中更新文档
尽管这种方式最初看起来有点??盾,但是实际上是可行的,而且非常容易。在从数据库读取 XML 文档并传递给应用程序的过程中,可以在查询中使用转换表达式修改 XML 文档。更新 2 是一个 SQL SELECT 语句,其中包含与 更新 1 相同的 XMLQUERY 函数、转换表达式和 WHERE 子句。更新 1 对磁盘上的文档执行永久的修改并在日志中记录这一修改,但是 更新 2 并不修改表中原来的文档,只是在查询处理过程中 “临时” 修改它。下面的更新 “掩盖” 电话号码的所有部分,只留下地区编码。
更新 2
select xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone with "905-xxx-xxxx"
return $new ')
from xmlcustomer
where cid = 1000;
|
本文中的大多数示例在 SQL UPDATE 语句中使用转换表达式。但是,转换表达式也可以出现在 更新 2 这样的查询中。本文后面的示例还说明查询中的转换表达式可以从文档中删除某些元素或属性,或者修改元素的名称。另外,在研究和测试新的更新功能时,在 SELECT 语句(而不是 UPDATE 语句)中嵌入包含转换表达式的 XMLQUERY 函数可能更方便。按照这种方式,可以马上看到和检验更新的效果,而且如果更新出现错误,也不会损坏表中的数据。
包含模式检验的更新
在修改 XML 文档时,可能希望检查它是否仍然符合给定的 XML 模式。更新 4 使用 XMLVALIDATE 函数根据 XML 模式("cust.custschema")检查更新后的文档是否是有效的。如果更新后的文档是无效的,UPDATE 语句就会失败。
更新 4
update xmlcustomer
set info = xmlvalidate(xmlquery('
copy $new := $INFO
modify do replace value of $new/customerinfo/phone
with "905-477-9011"
return $new ')
according to xmlschema id "cust.custschema")
where cid = 1000; |
XML 更新示例
到目前为止看到的示例都是使用 “replace value of” 操作修改元素节点的值。在下面的示例中,将看到如何执行其他操作,比如删除、插入和重命名 XML 文档中的元素和属性。
删除节点
更新 5 演示如何通过在 MODIFY 子句中使用 “delete” 操作从 XML 文档中删除 “phone” 元素。只需指定要从文档中删除的节点的路径。注意,除了 MODIFY 子句之外,其他代码都与前面讨论的更新相同。实际上,在后面的示例中,只修改 MODIFY 子句以表达不同类型的更新。
更新 5
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do delete $new/customerinfo/phone
return $new')
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9 |
|
所有节点级更新也可以应用于属性。更新 6 是一个删除属性(比如 “phone” 元素中的 “type” 属性)的示例。
更新 6
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do delete $new/customerinfo/phone/@type
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
重命名节点
可以使用 “rename” 操作给元素、属性或处理指令节点指定一个新名称。文本节点或 XML 注释等其他节点没有节点名称,因此无法重命名。更新 7 给出一个重命名示例,它把 “addr” 元素重命名为 “address”。这个更新并不使用 SQL WHERE 子句选择特定的一行。因此,修改表中的所有行(文档)。显然,这个操作花费的时间比只更新单一文档长。
更新 7
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do rename $new/customerinfo/addr as "address"
return $new' ) ; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John Smith
<address country="Canada">
FourthCalgaryAlbertaM1T 2A9address>
963-289-4136 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David Patterson FifthCalgaryAlbertaM1T 2A9 222-222-2222333-333-3333 |
|
替换节点
更新 8 演示如何使用 “replace” 操作把现有的 phone 元素替换为一个新元素。“replace” 操作的工作方式与 “replace value of” 操作不同。前者替换整个节点(旧的节点被删除),而后者只替换节点的内容。在 更新 8 中,在更新的 MODIFY 子句中提供完整的新的 “phone” 元素。
更新 8
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace $new/customerinfo/phone with
416-123-4567
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9416-123-4567 |
|
替换多个节点的值
可以在单一更新语句中对同一文档做多处修改。如 更新 9 所示,MODIFY 子句包含多个更新操作。它们被包围在圆括号中并由逗号分隔。这允许执行两个或更多相同类型或不同类型的更新操作。在 更新 9 中,两个更新操作都是 “replace value of” 类型的。它们分别替换 phone 元素和 @type 属性的值。有意思的是,这两个操作的最终效果与 更新 8 相同。
更新 9
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify (do replace value of $new/customerinfo/phone with "416-123-4567",
do replace value of $new/customerinfo/phone/@type with "home")
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9416-123-4567 |
|
插入节点
“insert?? 操作可以在 XML 文档中添加新节点,比如元素或属性。可以按照五种方式指定新节点在文档中的位置。现在假设希望在文档中插入一个新元素。那么,可以执行以下插入操作:
- insert element1 into element2
意味着 element1 将成为 element2 的子元素。element1 在 element2 的现有子元素中的位置是不确定的。
- insert element1 as last into element2
意味着 element1 将成为 element2 的最后一个子元素。
- insert element1 as first into element2
意味着 element1 将成为 element2 的第一个子元素。
- insert element1 after element2
意味着 element1 将成为 element2 的同胞元素,就出现在 element2 的后面。
- insert element1 before element2
意味着 element1 将成为 element2 的同胞元素,就出现在 element2 的前面。
如果插入新的 attribute 而不是 element1,那么操作 “into element2 ”、“as last into element2 ” 和 “as first into element2 ” 的效果是相同的,即属性都会被添加到 element2 中。注意,XML 数据模型并不为一个元素的属性定义位置次序。因此,last、first、before 和 after 并不影响属性的次序。如果在 element2 之前或之后插入一个 attribute,那么属性将被添加到 element2 的父元素中。
在上面列出的操作中,element1 也可以是运算结果为一系列节点的表达式。在这种情况下,这些节点都被插入指定的位置。另外,element1 可以是一个 XML 片段(其中包含嵌套的元素)的根,这样就可以插入整个片段。
现在,看几个示例。更新 10 在示例文档中插入第二个电话号码。通过指定 “into $new/customerinfo”,把 “customerinfo” 元素显式地指定为新的 “phone” 元素的父元素。
更新 10
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do insert 777-555-3333
into $new/customerinfo
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9416-123-4567777-555-3333 |
|
在 更新 10 的结果中,新的 “phone” 元素成为 “customerinfo” 的最后一个子元素。但是,只有像 更新 11 中那样显式地指定 “as last into”,才能保证这个位置。
更新 11
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do insert777-555-3333
as last into $new/customerinfo
return $new' )
where cid = 1000; |
|
如果希望让新的手机号码成为文档中的第一个 “phone” 元素,可以显式地要求把它插入到所有现有的 “phone” 元素前面,见 更新 12。
更新 12
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do insert 777-555-3333
before $new/customerinfo/phone[1]
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9777-555-3333416-123-4567 |
|
同样,可以把新的 “phone” 元素插入到客户名后面,见 更新 13。
更新 13
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do insert 777-555-3333
after $new/customerinfo/name
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John Smith777-555-3333FourthCalgaryAlbertaM1T 2A9416-123-4567 |
|
更新 14 在转换表达式中使用关系列 “CID” 生成新元素 “customerid” 并让它成为根元素 “customerinfo” 下的第一个元素。
更新 14
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do insert { $CID }
as first into $new/customerinfo
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
1000John SmithFourthCalgaryAlbertaM1T 2A9416-123-4567 |
|
如果希望 “customerid” 成为 “customerinfo” 元素的属性,那么插入表达式需要包含一个计算属性构造器。它包括关键字 attribute、所需的属性名和属性值的表达式,见 更新 15。
更新 15
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify do insert
attribute customerid { $CID }
into $new/customerinfo
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9416-123-4567 |
|
常见的问题及其解决方案
上面讨论的更新示例都非常简单直观。在开始编写更复杂的更新时,您可能会犯一些错误,DB2 可能会拒绝您的更新表达式。下面讨论常见的问题并提供简单的解决方案。
在文档节点中插入元素
XML 数据模型要求经过解析的 良构的 XML 文档有一个文档节点,文档节点是文档的根元素 (customerinfo) 的父元素。文档节点在 XML 文档的文本(序列化)表示中不可见。
一种常见的错误是在文档节点(而不是根元素)中插入新元素。更新 16 与 更新 10 几乎完全相同,只是插入操作指定为 “into $new” 而不是 “into $new/customerinfo”。这意味着 更新 16 插入新的 “phone” 元素作为 “customerinfo” 元素的同胞元素而不是子元素。结果是一个包含两个元素(customerinfo 和 phone)的 XML 序列,这不是良构的文档。因为 DB2 中的 XML 列只能包含良构的 XML,所以更新失败。
更新 16
update xmlcustomer
set info = xmlquery( '
copy $new := $INFO
modify do insert 777-555-3333 into $new
return $new' )
where cid = 1000; |
|
Error Message:
SQL20345N The XML value is not a well-formed document with a single root element.
SQLSTATE=2200L
|
|
原来的 XML 文档: | 被拒绝的 XML 值: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136777-555-3333 |
|
修改重复的节点
如果一个 XPath 表达式标识文档中的多个节点,它们就称为重复的节点。例如,示例表中的第二个文档包含两个 “phone” 元素。因此,表达式 /customerinfo/phone 代表包含两个元素的序列。对于节点序列,能够执行的操作只有 “delete” 操作 — 它会删除所有节点,见 更新 17。
更新 17
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do delete $new/customerinfo/phone
return $new' )
where cid = 1001; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9 |
|
如果只希望删除 “phone” 元素之一,那么可以在 MODIFY 子句中使用一个谓词,比如 modify do delete $new/customerinfo/phone[@type = "work"]。
更新 18 试图替换 “phone” 元素的值而不是删除它们。但是,DB2 在运行时发现有多个 “phone” 元素,因此返回错误 SQL16085N。如果在 DB2 命令提示上输入 ? SQL16085N,就会看到可能导致此错误的原因列表。错误消息中显示的原因码 “XUTY0008” 表示 “替换表达式的目标节点不是单一节点”。
更新 18
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone with "444-444-4444"
return $new')
where cid = 1001; |
|
Error message:
SQL16085N The target node of an XQuery "replace value of" expression is not valid.
Error QName=err:XUTY0008. SQLSTATE=10703 |
|
这个错误防止我们用同一个数字更新所有 “phone” 元素(这可能是没有意义的)。如果确实需要以相垃圾广告式更新多个节点,可以使用 XQuery “for” 表达式循环遍历这些节点,见 更新 19。
更新 19
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify for $j in $new/customerinfo/phone return
do replace value of $j with "444-444-4444"
return $new' )
where cid = 1001; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9444-444-4444
444-444-4444
|
|
但是在许多情况下,希望只修改多个节点之一。更新 20 通过在 MODIFY 子句中使用谓词只修改手机号码。
更新 20
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do replace value of $new/customerinfo/phone[@type = "cell"]
with "444-444-4444"
return $new' )
where cid = 1001; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222444-444-4444 |
|
尝试修改不存在的节点
在前一节中指出,在节点序列(比如重复元素)上只能应用删除操作。重命名和替换等其他操作需要用 “for” 表达式循环遍历受影响的节点(见 更新 19)。如果更新操作的目标是空的,也有相似的情况。例如,更新 21 尝试替换 @type = "home" 的 “phone” 元素的值。但是,示例文档不包含任何家庭电话号码,所以表达式 $new/customerinfo/phone[@type = "home"] 会产生空的结果。因此,更新 21 失败。尝试删除不存在的家庭电话号码的 “delete” 操作会成功,但是对文档没有影响。
更新 21
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone[@type = "home"]
with "777-777-7777"
return $new')
where cid = 1001; |
|
SQL16085N The target node of an XQuery "replace value of" expression is not
valid. Error QName=err:XUTY0008. SQLSTATE=10703 |
|
同样,原因码 “XUTY0008” 表示 “替换表达式的目标节点不是单一节点”。对于 更新 18,“不是单一节点” 意味着有多个节点。对于 更新 21,“不是单一节点” 意味着节点数少于 1,也就是没有节点。同样,可以使用 “for” 迭代解决这个问题。更新 22 会成功,但是对文档没有影响。
更新 22
update xmlcustomer
set info = xmlquery( '
copy $new := $INFO
modify for $j in $new/customerinfo/phone[@type = "home"] return
do replace value of $j with "777-777-7777"
return $new' )
where cid = 1001; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档(与更新之前相同): |
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
另一种方法是,使用 XQuery if-then-else 表达式有条件地更新家庭电话号码,如果家庭电话号码不存在,就插入新的号码。这种方法见 更新 23。
更新 23
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify if ($new/customerinfo/phone[@type="home"]) then
do replace value of $new/customerinfo/phone[@type="home"]
with "777-777-7777"
else do insert 777-777-7777
as last into $new/customerinfo
return $new' )
where cid = 1001; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333777-777-7777 |
|
多次修改同一个节点
在 更新 9 中已经看到,在一个更新语句的 MODIFY 子句中可以包含对同一文档的多个更新操作。但是,不能对同一个节点多次执行重命名、替换或修改。通常情况下,这样做没有任何意义。更新 24 给出一个示例。DB2 在运行时发现有针对同一 “phone” 元素的两个 “rename” 操作,所以这个更新失败。
更新 24
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify(
do rename $new/customerinfo/phone[@type="work"] as "telephone",
do rename $new/customerinfo/phone[. ="222-222-2222"] as "telephone" )
return $new')
where cid = 1001; |
|
Error Message:
SQL16083N Incompatible "rename" expressions exist in the modify clause of a
Transform. expression. Error QName=err:XUDY0015. SQLSTATE=10704 |
|
需要确保更新操作中的谓词选择不同的节点。在 更新 25 中,对于 $new/customerinfo/phone 表达式有两个 “replace value of” 操作。其中之一应用于工作电话,另一个应用于家庭电话,这会避免错误 SQL16083N。还使用与 更新 22 相似的 “for” 迭代,所以如果工作电话或家庭电话不存在,语句也不会失败。这种方法有助于处理可选元素和模式多样性。
更新 25
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify (for $x in $new/customerinfo/phone[@type="work"]
return do replace value of $x with "444-444-4444" ,
for $y in $new/customerinfo/phone[@type="home"]
return do replace value of $y with "555-555-5555")
return $new')
where cid = 1001; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
David PattersonFifthCalgaryAlbertaM1T 2A9222-222-2222333-333-3333 |
|
David PattersonFifthCalgaryAlbertaM1T 2A9444-444-4444333-333-3333 |
|
在 copy 子句中选择节点
到目前为止,本文中的所有更新示例都使用相同的 “copy” 子句,即 copy $new := $INFO。您可能会觉得奇怪,如果这个子句总是不变的,那么为什么需要提供这种语法。在某些情况下,“copy” 子句的灵活性确实能够提供方便。假设应用程序需要获取某位客户的信息,但是由于考虑到保护隐私,需要排除客户的电话号码。更新 26 和 更新 27 都能够实现这个目标。更新 26 使用 SQL 谓词选择文档,而 更新 27 使用 XQuery 谓词,完全避免使用 SQL。这些查询在处理期间 “临时” 转换文档,并不修改表中的文档。
更新 26
xquery
copy $new := db2-fn:sqlquery("select info from xmlcustomer where cid = 1000")
modify do delete $new/customerinfo/phone
return $new; |
|
查询结果: |
John SmithFourthCalgaryAlbertaM1T 2A9 |
|
更新 27
xquery
copy $new := db2-fn:xmlcolumn("XMLCUSTOMER.INFO")[/customerinfo/name="John Smith"]
modify do delete $new/customerinfo/phone
return $new; |
|
查询结果: |
John SmithFourthCalgaryAlbertaM1T 2A9 |
|
COPY 子句的右边部分必须只产生一项,比如一个客户。如果有多位客户名为 “John Smith” 或者没有名为 “John Smith” 的客户,DB2 就会产生以下错误:
SQL16084N An assigned value in the copy clause of a transform. expression is not a
sequence with exactly one item that is a node. Error QName=err:XUTY0013. SQLSTATE=10705 |
在 更新 28 中,通过循环遍历客户避免这个错误:
更新 28
xquery
for $i in db2-fn:xmlcolumn("XMLCUSTOMER.INFO")[/customerinfo/name="John Smith"]
return
copy $new := $i
modify do delete $new/customerinfo/phone
return $new; |
|
查询结果: |
John SmithFourthCalgaryAlbertaM1T 2A9 |
|
更新 29 给出另一个示例,它使用 COPY 子句只获取客户的地址并在 RETURN 子句中把修改后的地址嵌入新的 “sendto” 元素。
更新 29
xquery
for $i in db2-fn:xmlcolumn("XMLCUSTOMER.INFO")/customerinfo[name="John Smith"]
return
copy $new := $i/addr
modify do rename $new/zipcode as "postalcode"
return {$new}; |
|
原来的 XML 文档: | 查询结果: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
FourthCalgaryAlbertaM1T 2A9 |
|
在 SQL 更新语句中使用转换表达式时,很少在 COPY 子句中使用筛选谓词,通常是在 SQL WHERE 子句中进行筛选。
组合多个更新操作
即将形成的 XQuery 更新标准规定,MODIFY 子句中的所有更新操作相互独立地应用于文档。更新看不到其他更新的效果。这称为 “快照语义”,这意味着每个更新操作从逻辑上说应用于原文档的单独快照。更新 30 通过两个更新操作说明这种语义。第一个更新插入一个新的 “phone” 元素。第二个更新把所有 “phone” 元素重命名为 “telephone”。但是,重命名操作只应用于原文档中的 “phone” 元素,对于在同一个更新语句中添加的 “phone” 元素不起作用。更新操作在 MODIFY 子句中的次序并不影响结果。
更新 30
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify ( do insert 777-555-3333
after $new/customerinfo/addr ,
for $j in $new/customerinfo/phone
return do rename $j as "telephone" )
return $new' )
where cid = 1000; |
|
更新之前的 XML 文档: | 更新之后的 XML 文档: |
John SmithFourthCalgaryAlbertaM1T 2A9963-289-4136 |
|
John SmithFourthCalgaryAlbertaM1T 2A9777-555-3333963-289-4136 |
|
再举一个例子。假设一个更新语句删除 “addr” 元素并在 /customerinfo/addr 中插入新元素 “POBox”。新元素 “POBox” 不会出现在更新后的文档中,因为它的父元素 “addr” 被删除了。
如果产生的 XML 结构违反任何 XML 基本规则,更新就会被拒绝。例如,一个 XML 元素不能有两个名称相同的属性。如果试图在元素中添加与现有属性同名的属性,DB2 会拒绝执行更新。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15082138/viewspace-584367/,如需转载,请注明出处,否则将追究法律责任。