【发布时间】:2017-08-08 02:38:18
【问题描述】:
如何将 Postgres 的 XML 输出解析为 Linux 中 Basex 的输入?
【问题讨论】:
-
如果您要发布一个自我回答的问题,至少 尝试 实际提出一个问题...您的问题的主体不只是标记垃圾邮件你能想到的每一个产品。
标签: sql xml linux postgresql basex
如何将 Postgres 的 XML 输出解析为 Linux 中 Basex 的输入?
【问题讨论】:
标签: sql xml linux postgresql basex
哦,我知道答案可能已经过时了;但是我将把它留在这里,因为我认为您在回答中描述的方法可能对于手头的任务来说是矫枉过正的。
我不确定您是否有任何问题,但我想提出一种从根本上更精简的方法 ;-)
希望对你有帮助!玩得开心!
对于当前用例,您可能会丢弃awk、sed、postgres 和wget,您可以在 25 行 XQuery 中完成所有您需要的操作:
1) 一些基础知识,从远程服务器获取文件:
fetch:text('https://www.wien.gv.at/statistik/ogd/vie_101.csv')
2) 跳过第一行。 我决定使用原始文件附带的标头,但是您
fetch:text('https://www.wien.gv.at/statistik/ogd/vie_101.csv')
=> tokenize(out:nl()) (: Split string by newline :)
=> tail() (: Skip first line :)
=> string-join(out:nl()) (: Join strings with newline :)
所以总的来说,您的需求浓缩为:
RQ1.:
(: Fetch CSV as Text, split it per line, skip the first line: :)
let $lines := fetch:text('https://www.wien.gv.at/statistik/ogd/vie_101.csv')
=> tokenize(out:nl()) (: Split string by newline :)
=> tail() (: Skip first line :)
=> string-join(out:nl()) (: Join strings with newline :)
(: Parse the csv file, first line contains element names.:)
let $csv := csv:parse($lines, map { "header": true(), "separator": ";"})
for $record in $csv/csv/record
group by $date := $record/REF_DATE
order by $date ascending
return element year_total {
attribute date { $date },
attribute population { sum($record/POP_TOTAL) => format-number("0000000")}
}
问题 2.:
(: Fetch CSV as Text, split it per line, skip the first line: :)
let $lines := fetch:text('https://www.wien.gv.at/statistik/ogd/vie_101.csv')
=> tokenize(out:nl()) (: Split string by newline :)
=> tail() (: Skip first line :)
=> string-join(out:nl()) (: Join strings with newline :)
(: Parse the csv file, first line contains element names.:)
let $csv := csv:parse($lines, map { "header": true(), "separator": ";"})
for $record in $csv/csv/record
group by $date := $record/REF_DATE
order by $date ascending
return element year_total {
attribute date { $date },
attribute population { sum($record/POP_TOTAL) => format-number("0000000")},
for $sub_item in $record
group by $per-district := $sub_item/DISTRICT_CODE
return element district {
attribute name { $per-district },
attribute population { sum($sub_item/POP_TOTAL) => format-number("0000000")}
}
}
包括文件写入和以更易读的方式格式化的日期:
(: wrap elements in single root element :)
let $result := element result {
(: Fetch CSV as Text, split it per line, skip the first line: :)
let $lines := fetch:text('https://www.wien.gv.at/statistik/ogd/vie_101.csv')
=> tokenize(out:nl()) (: Split string by newline :)
=> tail() (: Skip first line :)
=> string-join(out:nl()) (: Join strings with newline :)
(: Parse the csv file, first line contains element names.:)
let $csv := csv:parse($lines, map { "header": true(), "separator": ";"})
for $record in $csv/csv/record
group by $date := $record/REF_DATE
order by $date ascending
return element year_total {
attribute date { $date => replace("^(\d{4})(\d{2})(\d{2})","$3.$2.$1")},
attribute population { sum($record/POP_TOTAL) => format-number("0000000")},
for $sub_item in $record
group by $per-district := $sub_item/DISTRICT_CODE
return element district {
attribute name { $per-district },
attribute population { sum($sub_item/POP_TOTAL) => format-number("0000000")},
$sub_item
}
}
}
return file:write("result.xml", $result)
【讨论】:
设置
数据来源:http://www.wien.gv.at/statistik/ogd/vie_101.csv 研究问题(RQ): RQ1:每次人口普查共有多少人住在维也纳? RQ2:每次人口普查,维也纳每个区有多少人居住?
准备
为了回答 RQ,选择了 postgre DB。秉承谚语“在哪里 有一个外壳,有一种方法”这段代码显示了 BASH 的一个简洁的解决方案(CLI Debian/Ubuntu 调味)。此外,在创建所需文件时,与 BASH 中的 postgre 交互要容易得多 用于进一步处理。关于安装过程请咨询: https://tecadmin.net/install-postgresql-server-on-ubuntu/
先用wget下载文件:
cd /path/to/directory/ ;
wget -O ./vie_101.csv http://www.wien.gv.at/statistik/ogd/vie_101.csv ;
然后使用您最喜欢的电子表格计算程序 (Libre Office Calc) 查看文件。 vie_101 应该是 UTF-8 编码并且可能使用半列 \;分隔符。打开, 检查,更改,保存。 需要进行一些重新格式化以方便下线处理。首先,创建一个头文件 具有适当的列名。二、下载的文件被“斩首”(前2行是 删除)和“剪切”(进入感兴趣的列)。最后附在头文件中。
echo 'DISTRICT,POPULATION,MALE,FEMALE,DATE' > ./vie.csv ;
declare=$(sed -e 's/,/ INT,/g' ./vie.csv)' INT' ;
sed 's/\;/\,/g' ./vie_101.csv | sed 's/\.//g' | tail -n+3 | cut -d ','
-f4,6-9 >> ./vie.csv ;
Postgre
为了将数据加载到 postgres 中,首先需要创建一个模式: echo "创建表 vie ($declare);" | sudo -u postgres psql ; 为了将数据实际加载到 postgres 之前创建和格式化的文件 (vie.csv) 需要由超级用户复制到 postgres 可以访问的文件夹中。只有那个副本 可以执行命令将数据加载到 postgres 中。需要注意的是root权限是 此操作需要 (sudo)。
sudo cp ./vie.csv /var/lib/postgresql/ ;
echo "\copy vie from '/var/lib/postgresql/vie.csv' delimiter ',' csv
header ;" | sudo -u postgres psql ;
XML 架构
在创建 XML 文档之前,我们必须设计文件的结构。我们决定 创建 XML 模式 (schema.xsd) 而不是 DTD。 我们的模式定义了一个根元素,它的子元素是复杂的元素。 该元素可以以任意数量出现。 element 的孩子是 , , , 和 。这5个元素(兄弟姐妹)很简单 元素和定义的值类型始终是整数。
使用 Postgre 创建 XML
由于最终目标是通过 xquery 回答 RQ,因此需要一个 xml 文件。这个文件 (xml.xml) 需要格式正确且格式正确。下一步,query_to_xml 命令通过管道传送到 postgres -Aqt 用于:
-A [aligned mode disable, remove header and + at end of line]
-q [quiet output]
-t [tuples only, removes footer]
echo "select query_to_xml( 'select * from vie order by date asc', true,
false, 'vie' ) ;" | sudo -u postgres psql -Aqt > ./vie_data.xml ;
现在,使用 table_to_xmlschema() 导出表的模式很重要。
echo "select table_to_xmlschema( 'vie', true, false, '') ;" | sudo -u
postgres psql -Aqt > ./vie_schema.xsd ;
这结束了 postgre 和 BASH 中的所有任务。作为最后一个命令 basex 可以启动。
basexgui
Xquery
使用 basex 可以很容易地通过以下模式验证 XML 文件: 验证:xsd('vie_data.xml','vie_schema.xsd') XML文件可以通过点击导入:
只能通过 for 循环按“日期”对数据进行分组来回答 RQ1。结果已保存 通过:
file:write( 'path/to/directory/file_name' ).
file:write( '/path/to/directory/population_year_total.xml',
for $row in //table/row
group by $date := $row/date
order by $date ascending
return <year_total date="{$date}"
population="{sum($row/population)}">
</year_total>)
通过嵌套两个 for 循环来回答 RQ2。外部循环按 DATE 分组并返回 每个给定日期的人口总数。内部循环按 DISTRICT 分组,因此,它返回一个 POPULATION 的子总和。
file:write( '/path/to/directory/district_year_subtotal.xml',
for $row in //table/row
group by $date:= $row/date
order by $date ascending
return <sub_sum date="{$date}"
population="{sum($row/population)}">{
for $sub_item in $row
group by $district := $sub_item/district
order by $district ascending
return <sub_item district="{$district}"
population="{sum($sub_item/population)}"/>
}</sub_sum>)
完成
【讨论】: