现在查询大约需要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中运行它
答案 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分钟后访问此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
提供了很多提示