PL SQL разбор GML - PullRequest
       3

PL SQL разбор GML

0 голосов
/ 26 февраля 2020

Попытка оптимизировать процедуру, которая анализирует строки 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 в 1 запрос. Но я не могу понять, как это сделать. Главным образом из-за изменяющегося уровня в XML элемента poslist и генерации номера внешнего / внутреннего кольца.

Необходимая информация:

  1. экстерьера или интерьера.
  2. Номер звонка. Так что я знаю, что координаты являются частью одного и того же внешнего / внутреннего кольца.
  3. [координаты]
  4. Имя непосредственного родителя элемента 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]

1 Ответ

2 голосов
/ 26 февраля 2020

Вы можете использовать цепочечные вызовы XMLTable:

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 получает внешний / внутренний вид узлов, добавляет порядковый столбец для «номера кольца» и включает в себя столбец 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;

which получает:

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() позволяет вам включать или опускать пространство имен из значения.


Вы также можете сделать это с одним XMLTable, но получение «номера звонка» немного проблематично; здесь я внедряю атрибут во внешний / внутренний узел - который может быть медленнее, чем цепной подход 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 l oop.

...