【问题标题】:Performance Issue of a Join in XqueryXquery 中连接的性能问题
【发布时间】:2021-06-23 18:04:26
【问题描述】:

我在多个级别执行连接时遇到了一个奇怪的性能问题,比如说我有一个巨大的 xml,我需要从中执行连接(在那个巨大的 xml 内的记录上,大约几千个)并定义 4 个层次结构级别(父级/儿童关系)。但是,三个级别的连接(每个级别一个连接)可以正常工作,但第四级连接需要几个小时才能完成。非常感谢任何输入和指导。谢谢。 例如:源 XML

<items>
<item>
<level>1</level>
<parentref></parentref>
<parentitem></parentitem>
<itemno>123</itemno>
<itemname>Laptop</itemname>
</item>
<item>
<level>2</level>
<parentref>1</parentref>
<parentitem>123</parentitem>
<itemno>111</itemno>
<itemname>Keyboard</itemname>
</item>
<item>
<level>2</level>
<parentref>1</parentref>
<parentitem>123</parentitem>
<itemno>112</itemno>
<itemname>Mouse</itemname>
</item>
<item>
<level>3</level>
<parentref>2</parentref>
<parentitem>112</parentitem>
<itemno>112-1</itemno>
<itemname>Fiber Mouse</itemname>
</item>
<item>
<level>4</level>
<parentref>3</parentref>
<parentitem>112-1</parentitem>
<itemno>112-2</itemno>
<itemname>Mouse Pad and USB</itemname>
</item>
</items>

预期输出:

<items>
<item>
<itemno>123</itemno>
<itemname>Laptop</itemname> -- Level 1 ( Top level always be one record)
<accessories>               ------------------------------- 
    <itemno>111</itemno>
    <itemname>Keyboard</itemname>  -- Both accessories belong to Level 2 ( can be any number of records)
</accessories>                        one of the accessories has two levels ( can be any number of records) 
<accessories>
    <itemno>112</itemno>
    <itemname>Mouse</itemname>
    <addons>
        <itemno>112-1</itemno>
        <itemname>Fiber Mouse</itemname>
        <moreaddons>                             --- Performance issue is at this level of join
            <itemno>112-2</itemno>                    For 500 addons records, I have almost 5000 moreaddons to match in my source xml.
            <itemname>Mouse Pad and USB</itemname>
        </moreaddons>
    </addons>
</accessories>          
</item>

我在下面使用的示例加入代码:

第 1 级:获取基础物品

for $x in input xml
where $x/level=1

第 2 级:获取给定基础项目的配饰

for $y in $x
for $i in input xml
where $i/level=2
and $y/itemno = $i/parentitem 

第 3 级:获取给定配件的插件

let $p := (for $bv in $i return $bv)
let $lst := (for $id in input xml
             where $ids/level=3 return $id)
return for-each($p,function($p){
if(for-each($lst,function($lst){$p/itemno=$lst/parentitem})=true()
then for $av in input xml
     where $av/level = 3
     and $av/parentitem=$p/itemno

 

第 4 级:获取给定插件的更多插件

let $q := (for $cv in $av return $cv)
let $lsts := (for $ids in input xml
             where $ids/level=4 return $ids)
return for-each($q,function($q){
if(for-each($lsts,function($lsts){$q/itemno=$lsts/parentitem})=true()
then for $dv in input xml
     where $dv/level = 4
     and $dv/parentitem=$q/itemno

【问题讨论】:

  • XQuery sn-ps 是否不完整?我没有看到任何else 表达式,似乎有些函数体没有关闭。
  • 对于高性能连接,您似乎希望在item 上为itemno 和level 建立一个索引。处理器是否识别或允许您声明它们取决于您使用的处理器。您使用哪种 XQuery 处理器?
  • 性能始终取决于 XQuery 实现,因此最重要的信息是告诉我们您使用的 XQuery 产品。
  • 感谢您的宝贵意见@Martin 和 Michael。是的,Intentionllay 我删除了 else 部分,因为它不包含代码中的任何条件。我使用支持 Xquery 1.0 和 3.0 版本的 Saxon Xquery 处理器。我会尝试这些代码 sn-ps 并再次感谢你们。
  • 在 Saxon 的情况下,了解哪个版本作为 Saxon EE(和 PE?)使用的查询优化器确实比 HE 更好。

标签: xml xslt xquery xquery-3.0


【解决方案1】:

直接嵌套的for..return 方法看起来像

<items>
{
    for $level1-item in /items/item[level = 1]
    return
        <item>
        {
            $level1-item!(itemno, itemname),
            for $level2-item in /items/item[level = 2 and parentitem = $level1-item/itemno]
            return
                <accessories>
                {
                    $level2-item!(itemno, itemname),
                    for $level3-item in /items/item[level = 3 and parentitem = $level2-item/itemno]
                    return
                        <addons>
                        {
                            $level3-item!(itemno, itemname),
                            for $level4-item in /items/item[level = 4 and parentitem = $level3-item/itemno]
                            return
                                <moreaddons>
                                {
                                    $level4-item!(itemno, itemname)
                                }
                                </moreaddons>
                        }
                        </addons>
                }
                </accessories>
        }
        </item>
}
</items>

最后嵌套要求一个函数采取一个级别:

declare variable $names as xs:string* external := ('item', 'acessories', 'addons', 'moreaddons');

declare function local:nest($root as document-node()) as element()*
{
    let $level := 1
    for $item in $root/items/item[level = $level]
    return 
        element { $names[$level] } {
           $item/itemno,
           $item/itemname,
           local:nest($root, $item, $level + 1)
        }
};

declare function local:nest($root as document-node(), $parent as element(item), $level as xs:integer) as element()*
{
    for $item in $root/items/item[level = $level and parentitem = $parent/itemno]
    return
        element { $names[$level] } {
           $item/itemno,
           $item/itemname,
           local:nest($root, $item, $level + 1)
        }
};

<items>
{
    local:nest(/)

}
</items>

【讨论】:

  • 嗨@Martin - 很抱歉花时间回复,直截了当的返回方法比我的代码效果好得多,并且从 4-5 小时大大减少到 20-30 分钟,但是我仍然觉得我还有一些改进的空间,就像你提到的添加索引一样。 xml 的大小几乎超过 600,000 行。因为,我是一个初学者,你能帮我分享一些关于 Xquery/Xpath 的教程,以获得一些基本的、高级的主题知识或对查询感到满意。感谢您对指导的所有帮助。
  • @Learner,为了学习 XQuery 3,我推荐 Priscilla Walmsley 所著的《XQuery: Search Across a Variety of XML Data》一书的最新版本。
  • 非常感谢@Martin
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多