【问题标题】:How to optimize a XQUERY in a SELECT statement?如何在 SELECT 语句中优化 XQUERY?
【发布时间】:2014-01-07 13:42:55
【问题描述】:

我正在使用 Oracle 数据库。我有一个表,其中一个列是 XMLTYPE。现在,问题陈述是我需要提取那些具有带有特定根元素和一个条件的 XML 的记录的计数。假设存储的 XML 格式如下:

<ns1:Warehouse whNo="102" xmlns:ns1="xyz">
  <ns1:Building></ns1:Building>
</ns1:Warehouse>

 <ns1:Warehouse whNo="102" xmlns:ns1="xyz">
  <ns1:Building>Owned</ns1:Building>
 </ns1:Warehouse>

除了 Warehouse 之外,还有其他带有 Root 元素的 XML

现在,我需要获取那些有

  1. 作为仓库的根元素
  2. 建筑元素为空

我编写了以下 SQL 查询:

select count(XMLQuery('declare namespace ns1="xyz.com";
                       for $i in /*      
                       where fn:local-name($i) eq "Warehouse"  
                       and fn:string-length($i/ns1:Building ) = 0
                       return <Test>{$i/ns1:Building}</Test>'
                       PASSING xml_response RETURNING CONTENT)) countOfWareHouses
 from test

这里,test是表的名字,*xml_response*是test表中XMLTYPE列的名字。

当记录较少时,此查询可以正常工作。我已经对表中大约 500 条记录进行了测试,所需时间约为 0.1 秒。但是随着表中记录数的增加,时间也会增加。当我将记录数增加到 5000 条时,所用时间约为 11 秒。对于当前存储的记录数为 185000 的生产表,此查询永远不会完成。

请帮助我优化此查询或 xquery。

编辑 1:

我试过用这个:

select count(XMLQuery(
         'declare namespace ns1 = "xyz";
         for $i in /
         return /ns1:Warehouse[not(ns1:Building/text())]'
         PASSING xml_response RETURNING CONTENT))
from test

 select count(XMLQuery(
         'declare namespace ns1 = "xyz";
         return /ns1:Warehouse[fn:string-length(ns1:Building)=0]'
         PASSING xml_response RETURNING CONTENT))
from test

但这不起作用。

当我尝试运行这些时,它会要求为 Building 和 Warehouse 提供绑定值。

【问题讨论】:

  • 您的 xquery 看起来太复杂了。我敢打赌,可以计算 xquery 中的空元素。这个answer 似乎解决了您的问题,但它正在计算非空元素。希望对您有所帮助!

标签: sql performance plsql xquery xquery-sql


【解决方案1】:

而不是你应该使用工作更快的谓词,例如:

ns1:Warehouse[string-length(ns1:Building)=0]

【讨论】:

  • 您好 Navin,感谢您的回复。但是,我尝试使用它。我不确定我是否正确使用它。我已使用编辑 1 编辑了原始问题。如果您可以检查并提出更改建议,那将非常有帮助。
【解决方案2】:

如果没有必要,不要使用local-name(...)。节点测试可能会更快并启用索引。您还可以删除string-length(...) 调用。

搜索&lt;Warehouse/&gt; 元素,它们的&lt;Building/&gt; 节点下方没有文本节点。如果您还想扫描任意子节点(包括属性!),请使用node() 而不是text()。如果您只是想确保某处的文本可能是其他节点的子节点,请改用ns1:Building//text(),例如在这种情况下:&lt;ns1:Building&gt;&lt;foo&gt;bar&lt;/foo&gt;&lt;/ns1:Building&gt;

这个简单的 XPath 表达式正在做你需要的事情:

/ns1:Warehouse[not(ns1:Building/text())]

如果您需要构造那些&lt;Test/&gt; 元素,请使用

for $warehouse in /ns1:Warehouse[not(ns1:Building/text())]
return <Test>{$warehouse/ns1:Building}</Test>

这应该是 XQuery 的真正替代品。

我刚刚意识到你想知道的只是数字,然后在 XQuery 中更好地计算(虽然我不能告诉你如何读取单个结果):

count(/ns1:Warehouse[not(ns1:Building/text())])

【讨论】:

  • 您好 Jens,感谢您的回复。但是,我尝试使用它。我不确定我是否正确使用它。我已使用编辑 1 编辑了原始问题。如果您可以检查并提出更改建议,那将非常有帮助。
  • 我刚刚意识到您一直在构建自己的结果元素。分别更新了我的答案。顺便说一句,如果您收到(错误)消息,请始终复制包含错误编号的准确消息,...请不要用自己的话重复。
  • Jens,创建自己的结果元素不一定是我想要的,因为我只需要计数。结果元素,如果可以避免的话,将是一个受欢迎的解决方案。
  • 使用我的最后一个查询,它只返回数字。在 XQuery 的“聪明”实现中(所有相关的都应该是),不会有任何中间结果。它将返回一个数字。
猜你喜欢
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-12
  • 2012-07-24
  • 1970-01-01
相关资源
最近更新 更多