优化慢查询xquery

时间:2016-01-04 20:22:41

标签: xml xpath xquery exist-db

现在查询大约需要2分钟,在我做了一些更改之前需要3:48米。

xml文档来自网页,因为它每5米更改一次,并实时提供有关总线的信息。

你能帮我优化这个查询吗?

xquery version "3.0";
declare namespace bus="http://docs.gijon.es/sw/busgijon.asmx";

declare function local:getNombreParada($numero)
{
    for $parada in doc("http://datos.gijon.es/doc/transporte/busgijoninfo.xml")//paradas/bus:parada
    where $numero=$parada/bus:idparada
    return $parada/bus:descripcion
};

declare function local:getBusesPorLinea($linea)
{

    let $numero:=$linea
    let $nBuses:=count(doc("http://datos.gijon.es/doc/transporte/busgijontr.xml")//bus:llegada[bus:idlinea=$numero])

    return 
    if($nBuses=0)
    then(<p>No hay ningun bus en esta linea</p>)
    else(
    <div>
        <h2>Numero de buses funcionando en la linea {$numero} : {$nBuses}</h2>

    <table class="table table-hover">
        <thead>
          <tr>
            <th>Parada</th>
            <th>Minutos hasta la llegada</th>
          </tr>
        </thead>
        <tbody>
            {
            for $l in doc("http://datos.gijon.es/doc/transporte/busgijontr.xml")//bus:llegada[bus:idlinea=$numero]
                for $parada in doc("http://datos.gijon.es/doc/transporte/busgijoninfo.xml")//paradas/bus:parada[bus:idparada=$l/bus:idparada]


            return <tr>
                        <td>{$parada/bus:descripcion}</td>
                        <td>{$l/bus:minutos}</td></tr>
            }
        </tbody>
    </table>

    </div>
    )


};

local:getBusesPorLinea(1)

PD:我在现有的Db中运行它

4 个答案:

答案 0 :(得分:2)

没有智能优化,这个连接表达式:

for $l in doc("a.xml")//bus:llegada[bus:idlinea=$numero]
  for $parada in doc("b.xml")//paradas/bus:parada[bus:idparada=$l/bus:idparada]
return <tr>...</tr>

将具有二次性能。你还没有告诉我们关于文件大小的任何信息,但这是我开始寻找的地方。

在XML数据库环境中处理此类问题的方法通常是创建适当的索引。

答案 1 :(得分:2)

首先,在eXist中优化查询的最佳方法是在本地存储XML并使用索引。请使用内置文档来了解如何设置索引。

但是,您的代码会遇到从网络上反复获取相同数据的问题。让我们来处理这个和另一个问题,你使用内存中的XML查询,这是另一个优化瓶颈。

最重要的第一步是使用您在本地数据库中查询的XML。与针对内存中XML节点的查询相比,数据库中节点的查询更快并且使用更少的内存。 (至少,版本高达2.2的情况就是这种情况。)

因此,这是一种在本地缓存数据的方法,在最新更新超过5分钟后刷新缓存。

xquery version "3.0";

declare namespace bus="http://docs.gijon.es/sw/busgijon.asmx";

(: Store the XML data in the collection /db/busgijon/data :)
declare variable $COL := "/db/busgijon/data";
declare variable $INFO-FILE := "busgijoninfo.xml";
declare variable $TR-FILE := "busgijontr.xml";

(: Fetch a page from cache or from web site, updating the cache :)
declare function local:fetchPage($filename) {
    (: If the page was fetched more than 5 minutes ago, refresh it :)
    let $expire := current-dateTime() - xs:dayTimeDuration('PT5M')
    let $page := doc($COL || "/" || $filename)/page
    return if (exists($page))
        then if ($page/xs:dateTime(@timestamp) ge $expire)
            then $page
            else (update replace $page/* with doc("http://datos.gijon.es/doc/transporte/" || $filename)/*
                , update value $page/@timestamp with current-dateTime()
                , $page)
        else doc(xmldb:store($COL, $filename, <page timestamp="{current-dateTime()}">{doc("http://datos.gijon.es/doc/transporte/" || $filename)/*}</page>))/page
};

declare function local:getBusesPorLinea($linea)
{
    (: Get the two pages from the database cache for querying :)
    let $info := local:fetchPage($INFO-FILE)/bus:BusGijonInfo
    let $tr := local:fetchPage($TR-FILE)/bus:BusGijonTr

    let $numero:=$linea
    let $nBuses:=count($tr//bus:llegada[bus:idlinea=$numero])

    return 
    if($nBuses=0)
    then(<p>No hay ningun bus en esta linea</p>)
    else(
    <div>
        <h2>Numero de buses funcionando en la linea {$numero} : {$nBuses}</h2>

    <table class="table table-hover">
        <thead>
          <tr>
            <th>Parada</th>
            <th>Minutos hasta la llegada</th>
          </tr>
        </thead>
        <tbody>
            {
            (: Loop through the TR page - fetched just once from cache :)
            for $l in $tr//bus:llegada[bus:idlinea=$numero]
                (: Loop through the Info page - fetched just once from cache :)
                for $parada in $info//paradas/bus:parada[bus:idparada=$l/bus:idparada]


            return <tr>
                        <td>{$parada/bus:descripcion}</td>
                        <td>{$l/bus:minutos}</td></tr>
            }
        </tbody>
    </table>

    </div>
    )


};

local:getBusesPorLinea(1)

我在本地更改的唯一部分是:getBusesPorLinea函数从缓存中获取顶部的两个文档,并使用嵌入循环中的文档。

local:fetchPage函数是大多数加速发生的地方。这是它的作用:

  • 将过期时间设置为过去5分钟。
  • 尝试从缓存中获取指定的页面。
  • 如果页面存在,请将获取的时间戳与到期时间戳进行比较。
  • 如果页面的时间戳小于5分钟(大于过期时间戳),则返回该页面。
  • 如果页面的时间戳大于5分钟前,请重新获取,使用刷新的文档更新页面内容,更新页面的时间戳,然后返回新页面。
  • 如果页面尚不存在,请使用当前时间戳将页面保存到指定的集合,并返回页面元素。

经过5分钟后访问此XQuery的第一个人将在刷新缓存时额外增加5-10秒。这允许缓存是被动的,因此您不必每五分钟手动刷新一次。

希望这有帮助。

答案 2 :(得分:1)

是否缓存了文档?我不是专家,但您的代码似乎多次访问同一文档。如果你确定内容是在执行环境中缓存的,那就没问题。否则我会尝试声明

declare variable $docinfo := doc("http://datos.gijon.es/doc/transporte/busgijoninfo.xml");
declare variable $doctr   := doc("http://datos.gijon.es/doc/transporte/busgijontr.xml");

确保只提取一次文件。

您还可以扫描文档至少两次以查找相同类型的数据。我会这样做一次:

declare variable $paradas  := $docinfo//paradas;
declare variable $llegadas := $doctr//bus:llegada;

然后只过滤集合:

declare function local:getNombreParada($numero)
{
    $paradas/bus:parada[bus:idparada = $numero]/bus:descripcion
};

declare function local:getBusesPorLinea($linea)
{
    let $numero:=$linea
    let $llegadasNum:=$llegadas[bus:idlinea=$numero]
    let $nBuses:=count($llegadasNum)

    return 

    if($nBuses=0)
    then(<p>No hay ningun bus en esta linea</p>)
    else(
    <div>
        <h2>Numero de buses funcionando en la linea {$numero} : {$nBuses}</h2>

    <table class="table table-hover">
        <thead>
          <tr>
            <th>Parada</th>
            <th>Minutos hasta la llegada</th>
          </tr>
        </thead>
        <tbody>
            {
            for $l in $llegadasNum
                for $parada in $paradas/bus:parada[bus:idparada=$l/bus:idparada]
                return <tr>
                        <td>{$parada/bus:descripcion}</td>
                        <td>{$l/bus:minutos}</td></tr>
            }
        </tbody>
    </table>

    </div>
    )
};

可能不是更快,但我希望它更具可读性。

答案 3 :(得分:1)

另一个提示:对于eXist-db中的查询,最好避免使用where子句。 XPath谓词通常表现得更好。

http://exist-db.org/exist/apps/doc/tuning.xml?q=performance&field=all&id=D2.2.2#D2.2.6

提供了很多提示