db2 查询xml_利用XML索引来提高DB2 9中的XML查询性能

本文深入探讨了DB2 9中XML查询性能的关键因素——XML索引。介绍了如何利用XML索引来优化查询,包括XQuery和SQL/XML语句的索引资格、执行计划、通配符、命名空间、数据类型匹配以及连接谓词的使用。文章通过示例展示了索引在处理通配符、命名空间和数据类型时的匹配要求,以及如何避免性能陷阱,强调了索引设计的重要性。
摘要由CSDN通过智能技术生成

db2 查询xml

DB2 9提供pureXML存储以及XML索引,XQuery和SQL / XML作为查询语言,XML模式支持以及对实用程序(例如Import / Export和Runstats)的XML扩展。 与关系查询一样,索引对于XQuery和SQL / XML语句的高性能至关重要。 DB2使您可以在XML列上定义特定于路径的 XML索引。 这意味着您可以使用它们为谓词和联接中常用的选定元素和属性建立索引。 例如,使用的样本数据如图1 ,下列指标idx1将是查找有用的起伏,并加入基于跨在XML列中的所有文档作者的ID bookinfo的表books

create table books(bookinfo XML);
create index idx1 on books(bookinfo) generate keys using xmlpattern '/book/authors/author/@id' as sql double;
图1.文本(序列化)格式和解析(层次结构)格式的样本XML文档
文本(序列化)格式和解析(层次结构)格式的样本XML文档

由于DB2不会强迫您将单个XML模式与XML列中的所有文档相关联,因此特定元素和属性的数据类型不是a-priori 。 因此,每个XML索引都要求您指定目标类型。 您将在本文后面看到类型为何重要的原因。 XML索引的可用数据类型为:

  • VARCHAR( n ) :对于具有已知最大长度n的字符串值的节点。
  • VARCHAR HASHED :用于具有任意长度的字符串值的节点。 该索引包含实际字符串的哈希值,并且只能用于相等谓词,而不能用于范围谓词。
  • DOUBLE :适用于任何数字类型的节点。
  • DATE和TIMESTAMP :对于具有日期或时间戳值的节点。

VARCHAR( n )索引的长度是一个硬约束。 如果您插入文档的索引元素或属性的值超过最大长度n ,则插入将失败。 同样,如果遇到大于n的值,则VARCHAR( n )索引的create index语句将失败。

默认情况下,DOUBLE,DATE或TIMESTAMP索引的数据类型不是硬约束。 例如,作者ID属性上的索引idx1被定义为DOUBLE,因为期望这些ID是数字值。 如果您插入的文档的作者ID为非数字值“ MN127”,则仍会插入该文档,但是值“ MN127”不会添加到索引中。 这是正确和安全的,因为DOUBLE索引只能评估无论如何永远不会匹配值“ MN127”的数字谓词。 因此,可以安全地从索引中忽略此值。

从DB2 9.5开始,您可以将可选子句REJECT INVALID VALUES添加到XML索引定义中。 此子句将索引的DOUBLE,DATE或TIMESTAMP类型强制作为硬约束。 如果定义以下索引,则不能插入作者ID为“ MN127”的文档,并且在创建该索引时XML列中不得存在该文档。

create index idx1 on books(bookinfo) 
generate keys using xmlpattern '/book/authors/author/@id' 
as sql double REJECT INVALID VALUES;

您可以在“ DB2 pureXML Cookbook ”中找到有关定义XML索引的更多详细信息。 在下面的XML索引用法讨论中,还假定您熟悉在DB2中查询XML数据的基本概念。 有关更多信息,请参考前面的文章“ 使用SQL查询DB2 XML数据 ”(developerWorks,2006年3月)和“ 使用XQuery查询DB2 XML数据 ”(developerWorks,2006年4月)以获取介绍,以及“ DB2中的pureXML”。 9:哪种方式查询XML数据? ”(developerWorks,2006年6月)以获取更多示例和详细信息。

XQuery和SQL / XML语句的XML索引资格

与关系查询一样,索引对于XQuery和SQL / XML语句的高性能至关重要。 当您的应用程序向DB2提交关系查询或XML查询时,查询编译器会将查询谓词与现有索引定义进行比较,并确定是否可以使用任何可用索引来执行查询。 此过程称为“索引匹配”,并为给定查询生成一组(可能为空)合格索引。 该集输入到基于成本的优化器,该优化器决定是否使用任何合格的索引。 在本文中,重点是索引匹配,而不是优化程序的索引选择。 除了运行“ runstats”为优化器提供有关数据的准确统计信息外,您对优化器的决定无能为力。 但是,您可以做很多事情来确保索引匹配。

在关系情况下,索引匹配通常很简单。 DB2可以使用在单个关系列上定义的索引来回答此列上的任何相等或范围谓词。 但是,对于XML列,这更加复杂。 关系列上的索引包含该列中的所有值,而XML索引仅包含与索引定义中的XML模式和数据类型均匹配的节点的值。 因此,仅当XML索引具有“正确”数据类型并且至少包含满足该谓词的所有XML节点时,才可以使用XML索引来评估XML查询谓词。 因此,XML索引资格有两个关键要求:

  1. XML索引定义与查询谓词(“包含”)具有同等或更少的限制。
  2. 索引的数据类型与查询谓词中的数据类型匹配。

本文介绍了如何设计XML索引和查询以确保满足这些要求,以及如何避免常见的陷阱。 首先要了解查询的执行计划。 DB2中现有的解释工具(例如Visual Explain和db2exfmt)可用于查看XQuery和SQL / XML的查询执行计划,就像查看传统SQL一样。

XML查询评估:执行计划和新运算符

为了执行XML查询,DB2 9引入了三个新的内部查询运算符,分别称为XSCAN,XISCAN和XANDOR。 这些新的运算符与现有的查询运算符(例如TBSCAN,FETCH和SORT)一起使DB2能够生成SQL / XML和XQueries的执行计划。 现在来看一下这三个新的运算符,以及它们如何与查询执行计划中的XML索引一起使用。

XSCAN(XML文档扫描)
DB2使用XSCAN运算符遍历XML文档树,并在需要时评估谓词并提取文档片段和值。 XSCAN 不是 “ XML表扫描”,但是它可以在表扫描之后处理执行每个文档时出现在执行计划中。

XISCAN(XML索引扫描)
与现有的关系索引关系索引扫描运算符(IXSCAN)一样,XISCAN运算符对XML索引执行查找或扫描。 XISCAN将值谓词作为输入,例如/book[price = 29]类的路径值对,或者where $i/book/price = 29 。 它返回一组行ID和节点ID。 行ID标识包含合格文档的行,节点ID标识这些文档中的合格节点。

XANDOR(XML索引AND'ing)
XANDOR运算符通过驱动多个XISCAN来同时评估两个或多个相等谓词。 它返回满足所有这些谓词的那些文档的行ID。

现在查看一个示例查询(以等效的XQuery和SQL / XML表示法),以了解没有索引,具有一个索引和具有多个索引的执行计划:

-- XQuery: 
xquery for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $i/book/title = "Database systems" and $i/book/price = 29
return $i/book/authors;
-- SQL/XML: select XMLQUERY('$i/book/authors' passing bookinfo as "i") from books where XMLEXISTS('$i/book[title = "Database systems" and price = 29]' passing bookinfo as "i");
-- Two Indexes: create index idx1 on books(bookinfo) generate keys using xmlpattern '/book/title' as sql varchar(50);
create index idx2 on books(bookinfo) generate keys using xmlpattern '/book/price' as sql double;

在图2中,您看到此查询的三种不同的执行计划(db2exfmt的简化输出)。 最好从树中最低的运算符读取此类执行计划,因为计划中的逻辑流程是从下至上,从左至右。

如果此查询中没有谓词的合格索引,则使用最左边的计划(a)。 表扫描运算符(TBSCAN)从表“ BOOKS”中读取所有行。 对于每一行,嵌套循环连接(NLJOIN)运算符将指向相应XML文档的指针传递给XSCAN运算符。 因此,NLJOIN不能用作具有两个输入分支的经典联接,但是可以方便XSCAN运算符访问XML数据。 XSCAN运算符遍历每个文档,评估谓词,如果满足谓词,则提取“ authors”元素。 RETURN运算符完成查询执行,并将查询结果返回给API。

图2.三种执行计划,(a)没有索引,(b)一个索引,(c)两个索引
三个执行计划,(a)没有索引,(b)一个索引,(c)两个索引

如果您有两个谓词之一的索引,例如/book/price上的索引idx1,您将看到类似于图2中的计划(b)的执行计划。XISCAN会使用路径值对( /book/price ,29),并返回价格为29的文档的行ID。对这些行ID进行排序,以删除重复项(如果有)并优化表的后续I / O。 然后,行ID扫描(RIDSCN)运算符将扫描这些行ID,触发行预取,并将行ID传递给FETCH运算符。 对于每个行ID,FETCH操作符将从表中读取相应的行。 该计划的好处在于,只检索表中的一小部分行,即仅检索“价格”为29的那些行。这比读取每一行的全表扫描便宜很多。 对于获取的每一行,XSCAN运算符都会处理相应的XML文档。 它评估“ title”上的谓词,如果满足该谓词,则提取“ authors”元素。 可能有很多文档中的第二个谓词不成立,并且XSCAN可能仍会进行大量工作以清除它们。 因此,如果第二个谓词也被索引覆盖,则可能会看到更好的性能。

如果您有两个谓词的索引,您可能会在图2中看到执行计划(c)。此计划使用两个XISCAN,每个谓词和索引一个。 XANDOR运算符使用这些XISCAN交替探查两个索引,以有效地找到与两个谓词匹配的文档的行ID。 然后,FETCH运算符仅检索这些行,从而最大程度地减少了对表的I / O。 对于每个文档,XSCAN随后都提取“ authors”元素。 如果谓词在路径中包含//或*,或者使用范围比较(例如<和>),则将看到索引AND'ing(IXAND)运算符,而不是XANDOR。 从逻辑上讲,两者都执行相同的工作,但是针对不同类型的谓词和具有不同的优化。

即使可以使用索引,优化器也可以决定不使用索引。 例如,如果第二个索引没有显着减少从表中检索的行数,例如访问索引的成本超过I / O节省的成本,则优化器可以选择计划(b)而不是计划(c)。桌子。 但是,您要确保优化器考虑所有符合条件的索引,然后选择成本最低,执行时间最短的计划。 换句话说,您要遵守XML索引资格的两个要求:

  • XML索引至少包含所有满足谓词的XML节点。
  • 查询谓词中的数据类型与索引定义兼容。

XML索引和查询谓词中的通配符

通配符//和*可能会影响索引和查询谓词之间的包含关系。 这是因为路径表达式(例如/book/price//price不同。 路径/book/price标识所有price元素,这些price元素是元素“ book”的直接子元素。 但是, //price路径标识XML文档中任何级别的任何位置的price要素。 因此, /book/price标识//price指定的元素的子集。 据说//price “包含” /book/price但并非相反。

现在看看这如何影响索引资格。 以以下查询为例。 它显示了表1中where子句的四个变体。

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $i/book/price = 29
return $i/book/authors

表1中最右边的两列表示两个替代的索引定义,并且表中的行显示了哪个谓词可以通过两个索引来评估(+)或不能(-)。 现在,逐步浏览表1中的行,以探索每个谓词的索引资格。

对于第一个谓词, /book/price上的索引是不合格的,因为它仅包含作为“ book”直接子代的“ price”元素。 索引不包含更深层次的“价格”元素,该元素可能存在于表中,并且可能与谓词路径$i//price匹配。 因此,如果DB2使用/book/price价格的索引,则它可能返回不完整的结果。 第二个索引//price符合条件,因为它包含谓词所需的文档任何级别的所有price要素。

第三个谓词使用了星号(*)作为通配符,这样它将查找的29值不仅是“价格”元素可以满足该谓词下的“书” 的任何子元素。 例如,元素/book/title值为29的文档将是有效的匹配项。 但是,表1的两个索引中的任何一个都不包含title元素。因此,两个索引都不能使用,因为DB2可能为该谓词返回不完整的结果。

表1. XML索引和谓词中具有通配符的索引资格
谓词/索引定义 ...使用xmlpattern' / book / price '作为sql double; ...使用xmlpattern' // price '作为sql double;
1个 $ i //价格= 29 -- +
2 其中$ i / book / price = 29 + +
3 其中$ i / book / * = 29 -- --
4 $ i / * / price = 29 -- +

第四个谓词$i/*/price = 29在任何根元素下查找价格元素,而不仅仅是“ book”。 如果存在带有/journal/price路径的文档,则它可能满足谓词$i/*/price = 29 ,但不会包含在/book/price的索引中。 因此,不能使用该索引,因为DB2再次冒着返回不完整查询结果的风险。 但是, //price上的索引包含任何价格元素,而与根元素无关。

简而言之,DB2查询编译器始终需要能够证明索引与谓词的限制相同或更少,以便它包含谓词要查找的所有内容。

请注意,在索引定义中使用通配符可能会无意中索引了比所需更多的节点。 建议尽可能使用指向索引定义和查询中所需元素或属性的确切路径,而不使用通配符。 可以使用非常通用的XML索引模式,例如//*//text() ,但应谨慎使用。 //*上的索引甚至可以索引非叶元素,这通常是没有用的,并且很容易超过Varchar( n )索引的长度约束。

XML索引和查询谓词中的命名空间

如果涉及名称空间,则需要注意XML索引的资格。 首先,如果表中的XML文档包含名称空间,则索引定义将需要考虑该名称空间。 这又归结为索引/谓词包含。 以以下XML文档和索引定义为例:

<bk:book xmlns:bk="http://mybooks.org">
   <bk:title>Database Systems</bk:title>
   <bk:price>29</bk:price>
</bk:book>
CREATE INDEX idx3 ON books(bookinfo)
GENERATE KEYS USING XMLPATTERN '/book/price' AS SQL DOUBLE;

该索引idx3不包含此样本文档的任何索引条目,因为它被定义为/book/price元素的索引,该名称空间为空。 但是,可以使用以下任何索引定义来正确索引价格元素:

CREATE INDEX idx4 ON books(bookinfo) GENERATE KEYS USING XMLPATTERN 
'declare namespace bk="http://mybooks.org"; /bk:book/bk:price' AS SQL DOUBLE

CREATE INDEX idx5 ON books(bookinfo) GENERATE KEYS USING XMLPATTERN 
'declare default element namespace "http://mybooks.org"; /book/price' AS SQL DOUBLE

CREATE INDEX idx6 ON books(bookinfo) GENERATE KEYS USING XMLPATTERN 
'/*:book/*:price' AS SQL DOUBLE

索引idx4明确声明名称空间和前缀以匹配文档。 索引idx5将名称空间声明为默认名称空间,因此由于隐含默认名称空间,因此在XML模式/book/price中不使用前缀。 索引idx6仅使用通配符来匹配任何名称空间。 在XQuery或SQL / XML语句中表达谓词时,可以使用相同的选项:

查询4:

-- XQuery:
XQUERY declare namespace bk="http://mybooks.org";
for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/bk:book
where $b/bk:price < 10
return $b
-- SQL/XML: select bookinfo from books where XMLEXISTS('declare namespace bk="http://mybooks.org"; $b/bk:book[bk:price < 10]' passing bookinfo as "b")

查询5:

-- XQuery:
XQUERY declare default element namespace "http://mybooks.org";
for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
where $b/price < 10
return $b
-- SQL/XML: select bookinfo from books where XMLEXISTS('declare default element namespace "http://mybooks.org"; $b/book[price < 10]' passing bookinfo as "b")

查询6:

-- XQuery:
XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/*:book
where $b/*:price < 10
return $b
-- SQL/XML: select bookinfo from books where XMLEXISTS('$b/*:book[*:price < 10]' passing bookinfo as "b")

表2对这三个查询的每一行都有一行,对于上面定义的索引idx3idx6每一列都有idx6 。 您可以在表2中进行一些重要观察。首先,没有名称空间的idx3不能用于任何考虑名称空间的查询。 接下来,您将看到查询4和查询5的行具有相同的条目,索引idx4idx5的列也具有相同的条目。 这是因为显式名称空间定义和默认名称空间定义在逻辑上是相同的,并且同一事物只是不同的表示法。 您可以使用任何一个而不影响索引匹配。 具有名称空间通配符的索引idx6可以用于所有示例查询,甚至可以用于没有名称空间的谓词,例如$b/price < 10 。 索引idx6也是唯一与查询6中的谓词匹配的索引。索引idx4idx5包含一个特定名称空间的索引条目,但不能用于在任何名称空间中查找书价的查询6。 因此,违反了收容要求。

表2. XML索引和谓词中具有名称空间的索引资格
查询/索引定义 idx3(无名称空间) idx4(显式命名空间) idx5(默认名称空间) idx6(命名空间通配符)
1个 Query4(显式命名空间) -- + + +
2 查询5(默认名称空间) -- + + +
3 查询6(命名空间通配符) -- -- -- +

XML索引和查询谓词中的数据类型

除了使用通配符和名称空间适当地包含索引或谓词外,对索引资格的第二个要求是谓词和索引的数据类型需要匹配。 在前面的所有示例中, /book/price元素始终被索引为DOUBLE。 但是,您可以决定将书本价格作为VARCHAR编制索引,如表3所示。但是,请注意,值谓词还具有由文字值的类型确定的数据类型。 双引号中的值始终是字符串,但不带引号的数字值将解释为数字。 如表3所示,字符串谓词只能用VARCHAR类型的XML索引求值,而数字谓词只能用DOUBLE类型的索引求值。

关系索引的数据类型始终由索引列的类型确定。 但是,由于DB2不会强迫您将XML模式与XML列相关联,因此元素或属性的数据类型不是预先确定的。 因此,每个XML索引都需要一个目标类型。 和类型很重要。 假设价格元素的值为9。字符串谓词“ 9” <“ 29”为false,而数字比较9 <29为true。 这说明如果要在语义上进行正确的数字比较,则应使用DOUBLE索引。 “价格”元素最好索引为DOUBLE。

表3. XML索引和谓词中的数据类型
谓词或索引定义 ...使用xmlpattern'/ book / price'作为sql double ; ...使用xmlpattern'/ book / price'作为sql varchar(10) ;
1个 其中$ i / book / price <“ 29” -- +
2 $ i /本/价格<29 + --

对XML连接谓词使用索引

在前面的示例中,您看到了包含谓词值的值谓词。 这些文字值确定比较的数据类型。 对于连接谓词,通常无法进行这种确定。 假设您有一个表格“ authors”,其中包含XML格式的详细作者信息,其中包括也在图书数据中出现的作者ID。 您想要使用联接来检索仅针对books表中书籍作者的详细作者数据。 在作者ID上定义索引似乎很有用:

create table books (bookinfo xml);
create table authors (authorinfo xml);

create index authorIdx1 on books(bookinfo) generate key using
xmlpattern '/book/authors/author/@id' as sql double;

create index authorIdx2 on authors(authorinfo) generate key using
xmlpattern '/author/@id' as sql double;

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO") 
    for $j in db2-fn:xmlcolumn("AUTHORS.AUTHORINFO") 
where $i/book/authors/author/@id = $j/author/@id
return $j;

该查询检索所需的作者信息,但不会将两个索引中的任何一个用于联接处理。 请注意,作者ID上的联接谓词不包含表示比较数据类型的文字值。 因此,DB2需要考虑任何数据类型的匹配作者ID。 例如,考虑表4中的书籍和作者数据。作者John Doe具有数字ID值(47),而作者Tom Noodle具有非数字ID值(TN28)。 两者在另一个表中都有有效的匹配项。 因此,这两个都需要包含在联接结果中。 但是,如果DB2使用数字索引authorIdx1authorIdx2 ,它将找不到作者ID“ TN28”并返回不完整的联接结果。 因此,DB2无法使用这些索引,而只能依靠表扫描来确保正确的查询结果。

样本书数据
<book>
    <authors>
     <author  id="47">John Doe</author>
    </authors>
    <title>Database Systems</title>
    <price>29</price>
</book>

<author id="47">
    <name>John Doe</name>
     <addr>
           <street>555 Bailey Av</street>
           <city>San Jose</city>
           <country>USA</country>
      </addr>
      <phone>4084511234</phone>
</author>
样本作者数据
<book>
    <authors>
     <author  id="TN28">Tom Noodle</author>
    </authors>
    <title>International Pasta</title>
    <price>19.95</price>
</book>

<author id="TN28">
    <name>Tom Noodle</name>
     <addr>
           <street>213 Rigatoni Road</street>
           <city>Toronto</city>
           <country>Canada</country>
      </addr>
      <phone>4162050745</phone>
</author>

但是,在许多情况下,文档的给定元素或属性中可能没有数字值和非数字值的混合。 如果您知道所有作者ID都是数字,则可以在查询中指出这一点,以允许DB2使用DOUBLE索引。 以下查询将联接谓词的两侧显式转换为DOUBLE。 这仅要求进行数字比较,而显式地忽略非数字联接匹配。 因此,DB2可以使用DOUBLE索引进行更快的联接处理。

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO") 
    for $j in db2-fn:xmlcolumn ("AUTHORS.AUTHORINFO") 
where $i/book/authors/author/@id/xs:double(.) = $j/author/@id/xs:double(.)  
return $j;

由于此查询不包含任何值谓词来限制book或authors表,因此DB2必须对两个表中的任何一个执行表扫描以读取所有作者ID。 对于每个作者ID,然后使用索引来探查该ID是否出现在另一个表中。 这比不使用任何索引的两个表扫描的嵌套循环联接要快得多。 DB2的基于成本的优化器确定要扫描的表以及要通过索引访问的表。 表5显示了这两个替代执行计划。 如果您通过以下两种方式之一用SQL / XML表示法编写相同的联接,则这两个执行计划也是可能的:

查询1:

select authorinfo
from books, authors
where xmlexists('$b/book/authors[author/@id/xs:double(.) = 
                               $a/author/@id/xs:double(.)]'
                 passing bookinfo as "b", authorinfo as "a");

查询2:

select authorinfo
from books, authors
where xmlexists('$a/author[@id/xs:double(.) = 
                         $b/book/authors/author/@id/xs:double(.)]'
                 passing bookinfo as "b", authorinfo as "a");

请注意,查询1和查询2在XMLEXISTS谓词的“方向”上有所不同。 在两个查询中,连接谓词均在方括号内表示。 在查询1中,方括号中的谓词是以$ b开头的表达式上的谓词,因此它是books表上的谓词。 在查询2中,连接条件是以$ a开头的表达式的谓词; 也就是authors表上的谓词。 DB2 9.7忽略了这种语法上的差异,并根据成本和基数估算值选择了表5中两个执行计划中较便宜的一个。

但是,在DB2 9.7之前,查询1和查询2中XMLEXISTS谓词的“方向”确定使用表5中的两个执行计划中的哪个。 由于查询1在books表上表达了连接谓词,因此DB2 9.1和9.5对authors执行表扫描,然后使用索引AUTHORIDX1探查books表。 在表5的左侧显示。

查询2将联接谓词应用于authors表。 因此,DB2 9.1和9.5对books执行表扫描,然后使用索引AUTHORIDX2探查authors表(表5的右侧)。 因此,编写XMLEXISTS谓词的方式会影响这些DB2早期版本中的连接顺序。 如果无法避免进行表扫描,请尝试使用较小的表。

db2exfmt生成的XML连接查询的执行计划:查询1
Rows
                     RETURN
                     (   1)
                      Cost
                       I/O
                       |
                  3.59881e-005
                     NLJOIN
                     (   2)
                     5410.62
                       743
                /-------+-------\
        1.29454e-007              278
           NLJOIN               NLJOIN
           (   3)               (   6)
           4311.96              1098.66
             570                  173
          /---+--\               /-+\
       556    2.32831e-010    139      2
     TBSCAN      XSCAN      FETCH   XSCAN
     (   4)      (   5)     (   7)  (  11)
     106.211     7.5643     47.237  7.56421
       14           1         34       1
       |                   /---+---\
       556              139          556
 TABLE: MATTHIAS      RIDSCN   TABLE: MATTHIAS
     AUTHORS          (   8)        BOOKS
                      15.2133
                         2
                        |
                        139
                      SORT
                      (   9)
                      15.2129
                         2
                        |
                        139
                      XISCAN
                      (  10)
                      15.1542
                         2
                        |
                        556
                  XMLIN: MATTHIAS
                    AUTHORIDX1
db2exfmt生成的XML连接查询的执行计划:查询2
Rows
                      RETURN
                      (   1)
                       Cost
                        I/O
                        |
                   8.37914e-015
                      NLJOIN
                      (   2)
                      5410.63
                        743
                /--------+--------\
        1.29454e-007           6.47269e-008
           NLJOIN                 NLJOIN
           (   3)                 (   6)
           4311.96                1098.67
             570                    173
          /---+--\               /---+--\
       556    2.32831e-010    139    4.65661e-010
     TBSCAN      XSCAN      FETCH       XSCAN
     (   4)      (   5)     (   7)      (  11)
     106.211     7.56429    47.2365     7.5643
       14           1         34           1
       |                   /---+---\
       556              139          556
 TABLE: MATTHIAS      RIDSCN   TABLE: MATTHIAS
      BOOKS           (   8)       AUTHORS
                      15.2128
                         2
                        |
                        139
                      SORT
                      (   9)
                      15.2124
                         2
                        |
                        139
                      XISCAN
                      (  10)
                      15.1537
                         2
                        |
                        556
                  XMLIN: MATTHIAS
                    AUTHORIDX2

要总结有关XML连接查询的建议,请始终将连接谓词强制转换为应使用的XML索引的类型。 否则,查询语义不允许使用索引。 如果XML索引定义为DOUBLE,则使用xs:double转换连接谓词。 如果XML索引定义为VARCHAR,则使用fn:string转换联接谓词,依此类推,如表4所示。(严格来说,DB2 9.7不必再使用fn:string来启用VARCHAR索引。联接谓词。但是,9.5中仍然需要它,并且在谓词中明确表示也无妨。)

表4.转换联接谓词以允许使用XML索引
索引SQL类型 使用以下方法强制转换联接谓词: 评论
xs:double 对于任何数值比较
VARCHAR(n),VARCHAR哈希 fn:字符串 对于任何字符串比较
日期 xs:date 用于日期比较
时间戳 xs:dateTime 对于时间戳谓词

索引支持“之间”谓词

XQuery没有类似于关系“之间”谓词的特殊功能或运算符。 此外,当您表达“介于”条件之间时,需要注意XQuery常规比较谓词的存在性。

假设您要查找价格在20到30之间的书籍。您可以直观地使用谓词/book[price > 20 and price < 30] ,但它并不构成“介于谓词之间”。 这意味着,如果您对/book/price拥有索引,则DB2无法执行从20到30的索引范围扫描来查找该价格范围内的书籍。 这是因为书文档可能具有多个价格子级,如以下示例所示:

<book>
   <title>Database Systems</title>
   <price currency="RMB">40</price>
   <price currency="USD">10</price>
</book>

由于一般比较(>,<,=,<=等)具有存在语义,因此,如果存在带有值的子元素“ price”,则谓词/book[price > 20 and price < 30]选择book元素。大于20,并且如果子元素“价格”的值小于30。这些元素可以是一个相同的元素,也可以是两个不同的“价格”元素。 上面的示例文档满足该谓词,因为价格大于20,并且(不同的)价格小于30。但是,这两个价格都不在20到30之间。

如果DB2使用从20到30的单个索引范围扫描,它将丢失该文档并返回不完整的查询结果。 取而代之的是,DB2需要计算两个索引扫描的交集,这通常会花费更多。 执行计划的差异如图3所示。左侧的执行计划显示了DB2捕获样本文档时必须考虑的索引AND'ing计划。 该计划效率不高,因为两个XISCAN都可能会产生大量的行ID,而上面的IXAND运算符需要消除其中的许多行ID。 这是因为许多书籍的价格都在20美元以上,而许多书籍的价格都在30美元以下。 实际上,两个XISCAN组合在一起产生的行ID比表中的行多。 这需要在IXAND运算符中进行繁重的工作,才能找到可能较小的交点。

如果您的意图是真正的“介于”谓词之间,那么右侧的执行计划会更好,因为带有起始-顶谓词的单个范围扫描仅提供匹配的行ID。 索引访问少得多,并且没有索引与运算,因此整体性能可以提高一到两个数量级-取决于谓词的选择性。

索引“与”与单范围扫描以评估一对范围谓词
RETURN 
                       |
                     NLJOIN 
                       |
                     /-+-\
                    /     \
                 FETCH    XSCAN  
                   |
               /---+---\
              /         \
          RIDSCN       TABLE:   
            |	      BOOKS
          SORT   
            |
         IXAND  
            |
        /---+---\
    XISCAN      XISCAN
 price > 20    price < 30
                    
                    


                  RETURN 
                    |
                  NLJOIN 
                    |
                  /-+-\
                 /     \
              FETCH    XSCAN  
                |
            /---+---\
           /         \
       RIDSCN       TABLE:   
         |	          BOOKS
       SORT   
         |
    XISCAN
 20 < price < 30

DB2编译器可以将同一数据项上的一对范围谓词解释为“之间”,并可以通过单个索引范围扫描来评估,只要DB2可以推断出该项是单例且不大于一个项目。 换句话说,谓词需要进行表述,以使之间的两个部分(>和<)始终应用于同一单个项目。 这可以通过值比较(>,<,=等),自身轴或属性来实现。

价值比较
如果您知道一本书最多包含一个价格要素,则可以使用XQuery值比较来编写查询,这会将比较操作数强制为单例。 例如, /book[price gt 20 and price lt 30]可以安全地解释为“介于两者之间”,并可以通过对价格指数进行一次范围扫描来进行评估。 如果遇到一本带有多个价格子元素的书,则查询将在运行时失败,并显示错误。

自轴
作为值比较的替代方法,可以使用自身轴(由点“。”表示)来表示“介于”谓词。 表达式/book/price[. > 20 and . < 30]的self轴/book/price[. > 20 and . < 30] /book/price[. > 20 and . < 30] /book/price[. > 20 and . < 30]确保两个谓词适用于相同的价格要素。 由于自身轴始终求值为单例,因此这构成了“介于”谓词。 该谓词允许一本书具有多个价格,但都要求所有价格的价格都在20到30之间。使用价值比较的好处是您不会冒运行时错误的风险。

属性
如果书籍价格恰好是一个属性,则每个书籍元素最多可以出现一次。 在表达式/book[@price>20 and @price<30]中,范围谓词的操作数为单例,因此DB2可以执行单个索引范围扫描以评估“之间”。

索引文本节点和XPath步骤“ / text()”

简要回顾一下什么是文本节点 。 图3显示了XML数据模型中的示例文档及其分层格式。 每个元素由一个元素节点表示,实际数据值由文本节点表示。 在XML数据模型中,元素的值定义为该元素下的子树中所有文本节点的串联。 因此,元素“ book”的值为“ Database Systems29”。 最低级别的元素的值等于其文本节点的值,例如,元素“ price”的值是“ 29”。

<book>
   <title>Database Systems</title>
   <price>29</price>
</book>
图3.示例文档的XML数据模型
示例文档的XML数据模型

XPath表达式/book/price/book/price/text()不同。 前者标识元素节点“价格”,后者标识值为“ 29”的文本节点。 因此,以下两个查询返回不同的结果。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
return $b/price

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
return $b/price/text()

第一个查询返回完整元素节点,即<price>29</price> ,而第二个查询仅返回其文本节点值29 。 如果在查询谓词中使用不带/text()的XPath表达式,例如以下查询中的$b/book ,则DB2将自动使用元素的值来评估谓词。 由于“ book”元素的值为“ Database Systems29”,因此该查询将返回样本文档作为有效匹配。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book = "Database Systems29"
return $b

但是,下一个查询/text()在where子句中的路径中添加了/text()的位置)不会返回示例文档。 这是因为元素“ book”下方没有文本节点。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book/text()= "Database Systems29"
return $b

因此,在一般情况下,查询语义取决于/text()的使用而不同。 带有/text()不带有/text()的最低级别的元素仅具有单个文本节点可能显示相同的行为。 例如,下面两个查询可能会返回相同的结果,但只有在查询执行过程中遇到的所有的“价格”元素都有一个文本节点,并没有其他的子节点。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book/price < 10
return $b

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book/price/text() < 10
return $b

由于/text()通常对查询语义有所影响,因此对索引资格也有影响。 在表5中,您可以看到带有/text()的谓词只能由在XML模式中也指定/text()的索引求值。 如果索引不使用/text() ,则谓词也不应使用/text()

表5.带有和不带有/ text()的索引和谓词
谓词或索引定义 ...使用xmlpattern'/ book / title / text() '作为sql varchar(128); ...使用xmlpattern'/ book / title'作为sql varchar(128);
其中$ i / book / title =“数据库系统” -- +
其中$ i / book / title / text()=“数据库系统” + --

为简单起见,建议您不要在XML索引定义或查询谓词中使用/text() 。 在XML模式//text()上定义索引以支持以/ text()结尾的任何路径表达式的谓词可能很诱人。 但是,这样的索引包含XML列中所有文档的所有文本节点值。 因此,在插入,更新和删除操作期间,索引将非常大且维护成本很高。 通常应避免使用这样的索引,除非您的应用程序大部分是只读的,并且您实际上无法预测在搜索条件中将使用哪些元素。

索引非叶元素

在上一节中,您在元素/book上看到了谓词,该谓词称为非叶(或非原子)元素,因为它包含其他元素。 尽管您可以在非叶元素上定义索引,但它们仅在极少数情况下有用。 考虑以下XML文档。 An index on the XML pattern /book would contain a single index entry for this document, and the value of that index entry is "John DoePeter PanDatabase Systems29SQLrelational." This is not useful since queries would typical not use such concatenated values in their predicates. Most indexes are always on leaf elements.

<book>
   <authors>
      <author id="47">John Doe</author>
      <author id="58">Peter Pan</author>
   </authors>
   <title>Database Systems</title>
   <price>29</price>
   <keywords>
      <keyword>SQL</keyword>
      <keyword>relational</keyword>
   </keywords>
</book>

There are a few cases where indexes on non-leaf elements can make sense. For example, assume your queries contain predicates on area codes and on full phone numbers. In that case, you could choose to design your phone elements as shown in this document.

<author  id="47">
    <name>John Doe</name>
    <phone>
           <areacode>408</areacode>
           <number>4511234</number>
    </phone>
</author>

Then, you can define one XML index on the non-leaf element "phone" and one on the element "areacode":

create index phoneidx on authors(authorinfo) generate key using
xmlpattern '/author/phone' as sql double;

create index areaidx on authors(authorinfo) generate key using
xmlpattern '/author/phone/areacode' as sql double;

This will allow both of the following queries to use indexed access rather than table scans.

select authorinfo from authors
where xmlexists('$a/author[phone=4084511234]' passing authorinfo as "a");

select authorinfo from authors
where xmlexists('$a/author[phone/areacode=408]' passing authorinfo as "a");

XML indexes cannot be composite key indexes like multi-column relational indexes. That is, you cannot define a single index on two or more XML patterns. However, you can sometimes mimic a composite index if your elements are appropriately nested. For example, the index phoneidx above acts much like a composite index on /phone/areacode and /phone/number .

Special cases where XML indexes cannot be used

Special cases with XMLQUERY and XMLEXISTS

All of the discussed guidelines for XML index eligibility apply to both XQuery and SQL/XML queries. Additionally, there are some specific considerations for the SQL/XML functions XMLQUERY and XMLEXISTS.

If you use XML predicates in the XMLQUERY function in the select clause of an SQL statement, these predicates do not eliminate any rows from the result set and therefore cannot use an index. They only apply to one document at a time and may return a (possibly empty) fragment of a document. Thus, you should place any document- and row-filtering predicates into an XMLEXISTS predicate in the where clause of your SQL/XML statement.

When you express predicates in XMLEXISTS, make sure that you use square brackets such as in $a/author[phone=4084511234] rather than $a/author/phone=4084511234 . The latter of these two predicates is a Boolean predicate that returns "false" if the phone element does not have the desired value. Since XMLEXISTS truly checks for the existence of a value, even the existence of the value "false" satisfies XMLEXISTS so that any document qualifies for the result set. If you use square brackets, then the XPath expression evaluates to the empty sequence that fails the existence test and eliminates the corresponding row (if the document doesn't have the desired phone number).

For more detailed examples of these XMLQUERY and XMLEXISTS semantics, refer to " 15 Best Practices for pureXML Performance in DB2 9 " (developerWorks, October 2006).

Let and return clauses

Be aware that predicates in XQuery let and return clauses do not filter result sets. Therefore, they cannot be expected to use indexes if element construction is involved. The next two queries cannot use an index because an element "phone408" needs to be returned for every author, even if it is an empty element for authors outside the 408 area code.

XQUERY for $a in db2-fn:xmlcolumn("AUTHORS.AUTHORINFO")/author
let $p := $a/phone[areacode="408"]//text()
return <phone408>{$p}</phone408>

XQUERY for $a in db2-fn:xmlcolumn("AUTHORS.AUTHORINFO")/author
return <phone408>{$a/phone[areacode="408"]//text()}</phone408>

Parent steps

DB2 9 also does not use an index for predicates that occur under a parent step (".."), such as the predicate on "price" in the following two queries:

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book/title[../price < 10]
return $b

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book/title 
where $b/../price < 10
return $b

This is not a significant limitation because you can always express these predicates without the parent axis:

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book[price < 10]/title
return $b

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
where $b/price < 10
return $b/title

A special case with the double-slash (//)

Another case to watch out for is when you use predicates with the descendant or self axis, commonly abbreviated by //. Assume you want to find books for the author with ID 129. If author ID attributes occur at multiple levels, or if you are unsure at which level or under which element the ID attributes are located, you may write the following unfortunate query:

WRONG!

select bookinfo
from books
where XMLEXISTS('$b/book/authors[//@id = 129]'
                 passing bookinfo as "b")

The intention of this query is to check ID attributes anywhere in or under the "authors" element. However, a slash or a double slash ( / or // ) at the front of a predicate in square brackets does not have a context and therefore refers to the root of the document. Thus, the query would return the following document as a result, which was not intended.

<book id="129">
    <authors>
      <author  id="47">John Doe</author>
      <author  id="58">Peter Pan</author>
    </authors>
    <title>Database Systems</title>
    <price>29</price>
</book>

To avoid this you need to add a dot (self axis) to indicate that you want to apply the descendant or self axis (//) from the "authors" element downward in the document tree.

RIGHT!

select bookinfo
from books
where XMLEXISTS('$b/book/authors[.//@id = 129]'
                 passing bookinfo as "b")

This also allows DB2 to use an index defined on /book//@id or //@id . No index is used if the dot is missing.

You can find further examples of how XQuery and SQL/XML language semantics effect index eligibility in the article " On the Path to Efficient XML Queries ."

摘要

XML indexes are critical for high XML query performance but wildcards, namespaces, data types, joins, text nodes, and other semantic aspects of XML queries determine whether a certain index can or cannot be used. Some attention is required to make sure that XML index definitions and query predicates are compatible. This article presented a set of guidelines and examples to show how XML indexes are used to avoid table scans and provide high query performance. The most important guidelines are summarized in the downloadable cheat sheet.

翻译自: https://www.ibm.com/developerworks/data/library/techarticle/dm-0611nicola/index.html

db2 查询xml

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值