商讯信箱
用户名: @
密  码:   注册|忘记密码
登录
个人用户经销商
您的位置:首页 > 技术频道 > 正文


提示 9:将文档过滤谓词放入 XMLEXISTS 中,而不是放入 XMLQUERY 中

让我们来考虑下面的表和数据:

create table customer(info XML);
            


表 2. customer 表中的三行数据
<customerinfo>
                        <name>Matt Foreman</name>
                        <phone>905-555-4789</phone>
                        </customerinfo>
                        

<customerinfo>
                        <name>Peter Jones</name>
                        <phone>905-123-9065</phone>
                        </customerinfo>
                        

<customerinfo>
                        <name>Mary Poppins</name>
                        <phone>905-890-0763</phone>
                        </customerinfo>
                        


对于这个表,假设您想返回电话号码为 “905-555-4789” 的客户的姓名。 您可能禁不住想编写下面这样的查询:

select xmlquery('$i/customerinfo[phone = "905-555-4789"]/name' passing info as "i")
            from customer;
            

但是,这个查询并不是您想要的,原因有好几个:

  1. 它返回下面这样的结果集,其中的行数与表中的行数一样多。这是因为 SQL 语句没有 where 子句,因此不能排除任何行。

    <name>Matt Foreman</name>


    3 record(s) selected


  2. 对于表中与谓词不匹配的每一行,返回一个包含空的 XML 序列的行。这是因为 XMLQUERY 函数中的 XQuery 表达式每次应用于一行(文档),并不会从结果集中去掉一行,只是修改它的。那个 XQuery 产生的值,当谓词为 true 时为客户的 name 元素,否则为空的序列。这些空行在语义上是正确的(根据 SQL/XML 标准),如果按这种方式编写查询,则必须返回它们。
  3. 该查询的性能并不好。首先,不能使用 /customerinfo/phone 上的索引,因为查询不允许排除行。其次,返回很多空行使查询速度不必要地慢了下来。

为了解决性能问题 得到所需的输出,应该在 select 子句中使用 XMLQUERY 函数,只提取客户姓名,将应该排除行的搜索条件转移到 where 子句的 XMLEXISTS 谓词中。这将允许使用索引和对行进行过滤,还可以避免空结果行带来的开销。像下面这样编写查询:


select xmlquery('$i/customerinfo/name' passing info as "i")
            from customer
            where xmlexists('$i/customerinfo[phone = "905-555-4789"]' passing info as "i")
            


<name>Matt Foreman</name>

1 record(s) selected

总而言之,XMLQUERY 函数中的谓词只应用于每个 XML 值当中,所以它们不会排除任何行。文档过滤和行过滤谓词应该放入到 XMLEXISTS 函数中。


提示 10:使用方括号 [ ] 来避免 XMLEXISTS 中的 Boolean 谓词

一种常见的错误是在 XMLEXISTS 函数中不使用方括号来编写前面的查询:

select xmlquery('$i/customerinfo/name' passing info as "i")
            from customer
            where xmlexists('$i/customerinfo/phone = "905-555-4789"' passing info as "i")
            

这将产生以下结果:

<name>Matt Foreman</name>
<name>Peter Jones</name>
<name>Mary Poppins</name>

3 record(s) selected

XMLEXISTS 谓词中的表达式使 XMLEXISTS 总是为 true。因此,没有行被排除。这是因为,对于一个给定的行,只有当里面的 XQuery 表达式返回空序列时,XMLEXISTS 谓词才为 false。然而,如果不使用方括号,XQuery 表达式就是一个总是返回 Boolean 值的 Boolean 表达式,而不会返回空序列。注意,XMLEXISTS 只是检查一个值的存在,如果存在一个值,即使这个值碰巧为 Boolean 值 “false”,XMLEXISTS 也将返回 true。虽然这并不是您想实现的效果,但是根据 SQL/XML 标准,这的确是正确的行为。

同样,其影响是不能使用 phone 上的索引,因为没有行被排除,所以会收到大量不需要的行。而且,在使用两个或更多谓词时,不要犯同样的错误,例如在下面这个查询中:


清单 6. XMLEXISTS 中两个谓词的不恰当的使用

            select xmlquery('$i/customerinfo/name' passing info as "i")
            from customer
            where xmlexists('$i/customerinfo[phone = "905-555-4789"] and
            $i/customerinfo[name = "Matt Foreman"]'
            passing info as "i")
            

这个查询使用了方括号,那么它错在哪里呢?XQuery 表达式仍然是一个 Boolean 表达式,因为它的形式是 “exp1 and exp2”。下面是编写这个查询的正确方式,这样编写查询可以过滤行,并允许使用索引:


清单 7. 可以过滤行并允许使用索引的正确查询

            select xmlquery('$i/customerinfo/name' passing info as "i")
            from customer
            where xmlexists('$i/customerinfo[phone = "905-555-4789" and name = "Matt Foreman"]'
            passing info as "i")
            

总而言之,在 XMLEXISTS 中不要使用 Boolean 谓词。将谓词放在方括号中,包括任何 “and” 和 “or”。


提示 11:使用 RUNSTATS 收集 XML 数据和索引的统计信息

RUNSTATS 实用程序已经被扩展,现在可以收集关于 XML 数据和 XML 索引的统计信息。DB2 基于成本的优化器使用这些统计信息为 XQuery 和 SQL/XML 查询生成有效的执行计划。因此,像对待关系数据那样,继续使用 RUNSTATS。如果表包含关系数据和 XML 数据,而您又只想刷新关系数据的统计信息,那么可以带新的子句 “EXCLUDING XML COLUMNS” 来执行 RUNSTATS。如果没有这个子句,缺省的也是可取的行为是总是收集关系数据和 XML 数据的统计信息。

对于关系数据和 XML 数据,可以启用抽样(sampling)来减少执行 runstats 的时间。在一个大型的数据集上,10% 的数据(或者更少)的统计信息通常仍然非常具有代表性。无论选择多大的抽样百分比,runstats 允许对行(Bernoulli sampling)或页面(system sampling)进行抽样。行级的抽样读取所有数据页,但是只考虑每页上一定百分比的行。而页级的抽样则可以显著减少 I/O,因为它只读取一定百分比的数据页。因此,如果表中不仅包含 XML 数据,而且还包含相当数量的关系数据,页抽样可以显著提高性能。但是,如果关系数据值是高度聚集的,那么行级抽样可以产生更精确的统计信息。

下面是一些例子。第一个 runstats 命令为表 customer 和它的所有索引收集最全面、最详细的统计信息,而没有采用抽样。如果执行时间允许的话,这样做是理想的。第二个命令收集同样的统计信息,但是只收集 10% 的页面的统计信息。在很多情况下,这样做可以为优化器提供接近于第一个命令的精确性的统计信息,但是可以更快地返回结果。第三个命令抽取 15% 的行,但是不收集分布统计信息,并对索引也应用了抽样,这不同于第一个命令和第二个命令。


清单 8. 使用 RUNSTATS 收集统计信息

            runstats on table myschema.customer
            with distribution on all columns and detailed indexes all;
            runstats on table myschema.customer
            with distribution on all columns and detailed indexes all tablesample system (10);
            runstats on table myschema.customer
            on all columns and sample detailed indexes all tablesample bernoulli (15);
            

总而言之,如果有可用的 XML 统计信息,DB2 可以生成更好的执行计划。像通常那样使用 runstats,或者利用抽样使用 runstats,以减少它的执行时间。


提示 12:如何使用 SQL/XML 发布视图将关系数据暴露为 XML

SQL/XML 发布函数允许将关系数据转换成 XML 格式。较好的做法是将 SQL/XML 发布函数藏在一个视图定义中,使应用程序和其他查询可以从视图中选择构造好的 XML 文档,而不必与发布函数本身打交道。


清单 9. 隐藏在视图中的 SQL/XML 发布函数

            create table unit( unitID char(8), name char(20), manager varchar(20));
            create view UnitView(unitID, name, unitdoc) as
            select unitID, name,
            XMLELEMENT(NAME "Unit",
            XMLELEMENT(NAME "ID", u,unitID),
            XMLELEMENT(NAME "UnitName", u.name),
            XMLELEMENT(NAME "Mgr", u.manager)
            )
            from unit u;
            

注意,我们在视图定义中包括了一些关系列。这并没有产生任何物理上的冗余,因为它只是一个视图,而不是物化的视图。暴露关系列有助于有效地查询这个视图。假设我们需要取一个对应于特定单位的 XML 文档。下面三个查询都可以实现这一点,但是第三个查询要好于前两个查询。

在前两个查询中,过滤谓词是在构造的 XML 上表达的。但是,XML 谓词不能应用于底层关系列或它的索引。因此,这些查询要求视图为所有单位构造 XML,然后选出对应于单位 “WWPR” 的 XML。这并非最佳方法。

以下查询可能取得次优性能:


清单 10. 次优的查询

            select unitdoc
            from UnitView
            where xmlexists('$i/Unit[ID = "WWPR"]' passing unitdoc as "i");
            for $u in db2-fn:xmlcolumn('UNITVIEW.UNITDOC')/UNIT
            where $u/ID = "WWPR"
            return $u;
            

第三种方法使用一个关系谓词来确保只为 “WWPR” 构造 XML 文档,从而减少了运行时间,对于大型的数据集,这种方法带来的性能好处尤其显著。这个查询可以取得很好的性能:


清单 11. 性能较好的查询

            select unitdoc
            from UnitView
            where UnitID = "WWPR";
            

总而言之,将关系列包括在 SQL/XML 发布视图中,在查询视图时,在那些关系列上表达谓词,而不是在构造的 XML 上表达谓词。


提示 13:如何使用 XMLTABLE 视图以关系格式暴露 XML 数据

创建一个视图以 XML 格式暴露关系数据,这样做很有用。同样,您可能也想使用一个视图将 XML 数据暴露为关系格式。提示 12 中的提醒也适用于这里的情况,只不过顺序要倒过来。让我们来看看下面的例子,其中使用了 SQL/XML 函数 XMLTABLE 以标量格式返回 XML 文档中的值:


清单 12. 以标量格式返回的 XML 文档中的值

            create table customer(info XML);
            create view myview(CustomerID, Name, Zip, Info) as
            SELECT T.*, info
            FROM customer, XMLTABLE ('$c/customerinfo' passing info as "c"
            COLUMNS
            "CID"     INTEGER      PATH './@Cid',
            "Name"    VARCHAR(30)  PATH './name',
            "Zip"     CHAR(12)     PATH './addr/pcode' ) as T;
            

注意,我们将 XML 列 info 包括在视图定义中,以便帮助有效地查询这个视图。假设您想基于给定的 ZIP 号码检索客户 ID 和姓名的一个标量列表。下面两个查询都可以实现这一点,但是第二个查询比第一个查询的性能要好。在第一个查询中,过滤谓词是在由 XMLTABLE 函数生成的 CHAR 列 “Zip” 上表达的。但是,关系谓词不能应用于底层的 XML 列或它的索引。因此,这个查询要求视图为所有 客户生成行,然后根据邮政编码 “95141” 选择符合条件的行。这不是最佳方法。第二个查询使用一个 XML 谓词来确保只生成对应于 “95141” 的行,从而减少了运行时间,对于大型的数据集,这种方法带来的性能好处尤其显著。


清单 13. 包含 XML 谓词的查询

            -- may perform suboptimal:
            select CustomerID, Name
            from myview
            where Zip = "95141";
            -- will perform well:
            select CustomerID, Name
            from myView
            where xmlexists('$i/customerinfo[addr/pcode = "95141"]' passing info as "i");
            

如果定义视图所用的基表不仅包含一个 XML 列,还包含有索引的关系列,那么应该将那些关系列包含在视图定义中。如果对视图的查询包含关系列上的具有高度限制性的谓词,那么 DB2 使用关系索引来过滤符合条件的行,得到较小数量的行,然后在返回最终结果集之前,对这个中间结果应用 XMLTABLE 和其他谓词。

总而言之,在使用 XMLTABLE 视图将 XML 数据暴露为关系格式时要加以小心。应该尽可能在视图定义中包含其他的列,以便在那些列上表达过滤谓词,而不是在 XMLTABLE 列上表达过滤谓词。

1 2 3 4 5
©版权所有。未经许可,不得转载。
[责任编辑:李宁]
[an error occurred while processing this directive]