问题描述
我正在尝试将 XQuery 结果写入 CSV 文件,请参阅附加代码(导致至少 160 万行,可能会变得更多..)。 然而,程序在执行几分钟后失败,并出现“主内存不足”错误。我使用的是 4GB 内存的笔记本电脑。我原以为写入文件可以防止内存瓶颈。另外,我已经在使用 copynode-false pragma。
我可能以错误的方式处理代码,因为这是我的第一个 XQuery/BaseX 程序。或者这可能在没有额外硬件的情况下无法解决..(当前数据库大小:3092 MB;节点:142477344)任何帮助将不胜感激!
let $params :=
<output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
<output:method value="csv"/>
<output:csv value="header=yes,separator=semicolon"/>
</output:serialization-parameters>
return file:write(
'/tmp/output.csv',(# db:copynode false #){<csv>{
for $stand in //*:stand
return <record>{$stand//*:kenmerk}</record>
(: {$stand//*:identificatieVanVerblijfsobject}
{$stand//*:inOnderzoek}
{$stand//*:documentdatum}
{$stand//*:documentnummer} :)
}</csv>},$params
)
解决方法
使用 copynode
编译指示来节省内存是个好主意。在给定的情况下,可能是新创建的元素节点的总数在将数据写入磁盘之前只会消耗过多的内存。
如果您有大型数据集,xquery
序列化格式可能是更好的选择。映射和数组比 XML 节点消耗更少的内存:
let $params := map {
'format': 'xquery','header': true(),'separator': 'semicolon'
}
let $data := map {
'names': [
'kenmerk','inOnderzoek'
],'records': (
for $stand in //*:stand
return [
string($stand//*:kenmerk),string($stand//*:inOnderzoek)
]
)
}
return file:write-text(
'/tmp/output.csv',csv:serialize($data,$params)
)
另一种方法是使用 window
子句并将结果写成块:
for tumbling window $stands in //*:stand
start at $s when true()
end at $e when $e - $s eq 100000
let $first := $s = 1
let $path := '/tmp/output.csv'
let $csv := <csv>{
for $stand in $stands
return <record>{
$stand//*:kenmerk,$stand//*:inOnderzoek
}</record>
}</csv>
let $params := map {
'method': 'csv','csv': map {
'separator': 'semicolon','header': $first
}
}
return if ($first) then (
file:write($path,$csv,$params)
) else (
file:append($path,$params)
)
第一次写操作后,后续的表行会追加到原文件中。块大小(此处:每个循环 100000 行)可以自由调整。与您的原始代码类似,序列化参数也可以指定为 XML;当然也可以使用第二个示例中的 xquery
序列化格式。