PLSQL解析GML
PLSQL parsing GML
正在尝试优化将 GML 字符串解析为 Oracle sdo_geometry 的过程。
<gml:Polygon >
<gml:exterior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:exterior>
<gml:interior>
<gml:Ring>
<gml:curveMember>
<gml:Curve>
<gml:segments>
<gml:LineStringSegment>
<gml:posList > [coordinates] </gml:posList>
</gml:LineStringSegment>
<gml:Arc>
<gml:posList > [coordinates] </gml:posList>
</gml:Arc>
<gml:LineStringSegment>
<gml:posList > ... </gml:posList>
</gml:LineStringSegment>
</gml:segments>
</gml:Curve>
</gml:curveMember>
</gml:Ring>
</gml:interior>
<gml:interior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:interior>
</gml:Polygon>
多边形由 1 个外部元素和 0 个内部元素组成。
我们当前的解决方案如下。
首先它提取所有外部和内部元素并分别处理它们
cursor c_exterior(p_xml xmltype) is
select t.*
from xmltable('//exterior' passing p_xml columns exterior xmltype path '/') as t;
cursor c_interior(p_xml xmltype) is
select t.*
from xmltable('//interior' passing p_xml columns interior xmltype path '/') as t;
begin
-- process each exterior/interior ring.
for r_exterior in c_exterior(p_xml)
loop
process(r_exterior.exterior, [other params]);
end loop;
for r_interior in c_interior(p_xml)
loop
process(r_interior.interior, [other params]);
end loop;
外部和内部元素的处理如下:
select t.*
from xmltable('for $d in //node() where exists($d/posList) return $d' passing p_xml columns
poslist clob path './posList'
,parent varchar2(100) path 'name()') as t;
这将获取坐标列表和 poslist 元素的直接父级的名称。
问题:由于这是一个两步过程,因此可能未针对速度进行优化。我正在寻找从 GML in 1 查询中获取所需信息的方法。但我不知道该怎么做。主要是因为 poslist 元素 XML 的不同级别和生成 exterior/interior 环的数量。
我需要的信息:
- 外部或内部。
- 环数。所以我知道坐标是
同一 exterior/interior 戒指的一部分。
- [坐标]
- poslist 元素的直接父元素的名称。
所以从上面的例子来看:
exterior, 1, gml:LinearRing, [coordinates]
interior, 2, gml:LineStringSegment, [coordinates]
interior, 2, gml:Arc, [coordinates]
interior, 2, gml:LineStringSegment, [coordinates]
interior, 3, gml:LinearRing, [coordinates]
您可以使用链式 XML表调用:
select x1.type, x1.ring_num, x2.parent, x2.coordinates
from xmltable(
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/Polygon/*'
passing p_xml
columns type varchar2(8) path './local-name()',
ring_num for ordinality,
nodes xmltype path '//posList/..'
) x1
cross join xmltable (
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/*'
passing x1.nodes
columns parent varchar2(21) path './name()',
coordinates varchar2(30) path 'posList'
) x2;
(当然,根据实际数据的需要调整输出数据类型和大小)
x1 table 获取 exterior/interior 节点,为 'ring number' 添加序数列,并包含一个 XMLType 列,其中包含所有节点posList
节点的父节点。然后将其传递给 x2,后者提取多个 posList
坐标和父项。
演示使用 CTE 提供 XML 文档而不是 PL/SQL:
with t (p_xml) as (
select xmltype('<gml:Polygon xmlns:gml="http://www.opengis.net/gml/">
<gml:exterior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:exterior>
<gml:interior>
<gml:Ring>
<gml:curveMember>
<gml:Curve>
<gml:segments>
<gml:LineStringSegment>
<gml:posList > [coordinates] </gml:posList>
</gml:LineStringSegment>
<gml:Arc>
<gml:posList > [coordinates] </gml:posList>
</gml:Arc>
<gml:LineStringSegment>
<gml:posList > ... </gml:posList>
</gml:LineStringSegment>
</gml:segments>
</gml:Curve>
</gml:curveMember>
</gml:Ring>
</gml:interior>
<gml:interior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:interior>
</gml:Polygon>') from dual
)
select x1.type, x1.ring_num, x2.parent, x2.coordinates
from t
cross join xmltable(
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/Polygon/*'
passing t.p_xml
columns type varchar2(8) path './local-name()',
ring_num for ordinality,
nodes xmltype path '//posList/..'
) x1
cross join xmltable (
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/*'
passing x1.nodes
columns parent varchar2(21) path './name()',
coordinates varchar2(30) path 'posList'
) x2;
得到:
TYPE RING_NUM PARENT COORDINATES
-------- ---------- --------------------- ------------------------------
exterior 1 gml:LinearRing [coordinates]
interior 2 gml:LineStringSegment [coordinates]
interior 2 gml:Arc [coordinates]
interior 2 gml:LineStringSegment ...
interior 3 gml:LinearRing [coordinates]
在 name()
和 local-name()
之间切换可让您在值中包含或省略命名空间。
您也可以使用单个 XML 表来执行此操作,但是获取 'ring number' 有点麻烦;在这里,我将一个属性注入 exterior/interior 节点 - 这可能比链式 XMLTable 方法慢,但可能值得对您的数据进行测试:
select x.type, x.ring_num, x.parent, x.coordinates
from xmltable(
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'copy $i := /Polygon
modify
for $j in $i/(exterior | interior)
return (insert node attribute pos { count($j/preceding-sibling::*) + 1 } into $j)
return $i//posList'
passing t.p_xml
columns type varchar2(8) path 'local-name(./ancestor::exterior | ./ancestor::interior)',
ring_num number path '(./ancestor::exterior/@pos | ./ancestor::interior/@pos)',
parent varchar2(21) path '../name()',
coordinates varchar2(30) path '.'
) x;
再次使用样本数据的 CTE 得到相同的结果。我不确定是否有另一种方法可以有效地获取 FLWOR 循环中的迭代计数。
正在尝试优化将 GML 字符串解析为 Oracle sdo_geometry 的过程。
<gml:Polygon >
<gml:exterior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:exterior>
<gml:interior>
<gml:Ring>
<gml:curveMember>
<gml:Curve>
<gml:segments>
<gml:LineStringSegment>
<gml:posList > [coordinates] </gml:posList>
</gml:LineStringSegment>
<gml:Arc>
<gml:posList > [coordinates] </gml:posList>
</gml:Arc>
<gml:LineStringSegment>
<gml:posList > ... </gml:posList>
</gml:LineStringSegment>
</gml:segments>
</gml:Curve>
</gml:curveMember>
</gml:Ring>
</gml:interior>
<gml:interior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:interior>
</gml:Polygon>
多边形由 1 个外部元素和 0 个内部元素组成。
我们当前的解决方案如下。
首先它提取所有外部和内部元素并分别处理它们
cursor c_exterior(p_xml xmltype) is
select t.*
from xmltable('//exterior' passing p_xml columns exterior xmltype path '/') as t;
cursor c_interior(p_xml xmltype) is
select t.*
from xmltable('//interior' passing p_xml columns interior xmltype path '/') as t;
begin
-- process each exterior/interior ring.
for r_exterior in c_exterior(p_xml)
loop
process(r_exterior.exterior, [other params]);
end loop;
for r_interior in c_interior(p_xml)
loop
process(r_interior.interior, [other params]);
end loop;
外部和内部元素的处理如下:
select t.*
from xmltable('for $d in //node() where exists($d/posList) return $d' passing p_xml columns
poslist clob path './posList'
,parent varchar2(100) path 'name()') as t;
这将获取坐标列表和 poslist 元素的直接父级的名称。
问题:由于这是一个两步过程,因此可能未针对速度进行优化。我正在寻找从 GML in 1 查询中获取所需信息的方法。但我不知道该怎么做。主要是因为 poslist 元素 XML 的不同级别和生成 exterior/interior 环的数量。
我需要的信息:
- 外部或内部。
- 环数。所以我知道坐标是 同一 exterior/interior 戒指的一部分。
- [坐标]
- poslist 元素的直接父元素的名称。
所以从上面的例子来看:
exterior, 1, gml:LinearRing, [coordinates]
interior, 2, gml:LineStringSegment, [coordinates]
interior, 2, gml:Arc, [coordinates]
interior, 2, gml:LineStringSegment, [coordinates]
interior, 3, gml:LinearRing, [coordinates]
您可以使用链式 XML表调用:
select x1.type, x1.ring_num, x2.parent, x2.coordinates
from xmltable(
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/Polygon/*'
passing p_xml
columns type varchar2(8) path './local-name()',
ring_num for ordinality,
nodes xmltype path '//posList/..'
) x1
cross join xmltable (
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/*'
passing x1.nodes
columns parent varchar2(21) path './name()',
coordinates varchar2(30) path 'posList'
) x2;
(当然,根据实际数据的需要调整输出数据类型和大小)
x1 table 获取 exterior/interior 节点,为 'ring number' 添加序数列,并包含一个 XMLType 列,其中包含所有节点posList
节点的父节点。然后将其传递给 x2,后者提取多个 posList
坐标和父项。
演示使用 CTE 提供 XML 文档而不是 PL/SQL:
with t (p_xml) as (
select xmltype('<gml:Polygon xmlns:gml="http://www.opengis.net/gml/">
<gml:exterior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:exterior>
<gml:interior>
<gml:Ring>
<gml:curveMember>
<gml:Curve>
<gml:segments>
<gml:LineStringSegment>
<gml:posList > [coordinates] </gml:posList>
</gml:LineStringSegment>
<gml:Arc>
<gml:posList > [coordinates] </gml:posList>
</gml:Arc>
<gml:LineStringSegment>
<gml:posList > ... </gml:posList>
</gml:LineStringSegment>
</gml:segments>
</gml:Curve>
</gml:curveMember>
</gml:Ring>
</gml:interior>
<gml:interior>
<gml:LinearRing>
<gml:posList > [coordinates] </gml:posList>
</gml:LinearRing>
</gml:interior>
</gml:Polygon>') from dual
)
select x1.type, x1.ring_num, x2.parent, x2.coordinates
from t
cross join xmltable(
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/Polygon/*'
passing t.p_xml
columns type varchar2(8) path './local-name()',
ring_num for ordinality,
nodes xmltype path '//posList/..'
) x1
cross join xmltable (
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'/*'
passing x1.nodes
columns parent varchar2(21) path './name()',
coordinates varchar2(30) path 'posList'
) x2;
得到:
TYPE RING_NUM PARENT COORDINATES
-------- ---------- --------------------- ------------------------------
exterior 1 gml:LinearRing [coordinates]
interior 2 gml:LineStringSegment [coordinates]
interior 2 gml:Arc [coordinates]
interior 2 gml:LineStringSegment ...
interior 3 gml:LinearRing [coordinates]
在 name()
和 local-name()
之间切换可让您在值中包含或省略命名空间。
您也可以使用单个 XML 表来执行此操作,但是获取 'ring number' 有点麻烦;在这里,我将一个属性注入 exterior/interior 节点 - 这可能比链式 XMLTable 方法慢,但可能值得对您的数据进行测试:
select x.type, x.ring_num, x.parent, x.coordinates
from xmltable(
xmlnamespaces(default 'http://www.opengis.net/gml/'),
'copy $i := /Polygon
modify
for $j in $i/(exterior | interior)
return (insert node attribute pos { count($j/preceding-sibling::*) + 1 } into $j)
return $i//posList'
passing t.p_xml
columns type varchar2(8) path 'local-name(./ancestor::exterior | ./ancestor::interior)',
ring_num number path '(./ancestor::exterior/@pos | ./ancestor::interior/@pos)',
parent varchar2(21) path '../name()',
coordinates varchar2(30) path '.'
) x;
再次使用样本数据的 CTE 得到相同的结果。我不确定是否有另一种方法可以有效地获取 FLWOR 循环中的迭代计数。