Список узлов SQL XML, разделенных запятой

У меня есть обычная таблица SQL, один из столбцов представляет собой XML, например:

...
<Element><id>first</id></Element>
<Element><id>second</id></Element>
...

Мне нужно получить список идентификаторов, разделенных запятыми:

id_list
---
first,second

На данный момент я добился этого, создав XMLTABLE с идентификаторами:

id
----
first
second

а затем с помощью функции LISTAGG Oracle. Интересно, существует ли какая-то функция/цикл (может быть, FLWOR?), чтобы получить тот же результат, но без преобразования ввода XML в XMLTABLE.

Большое спасибо за помощь


person Luca Di Gregorio    schedule 30.04.2017    source источник
comment
Почему? То, как вы это делаете сейчас, является естественным или нормальным способом; почему вы хотите сделать это по-другому? Это можно сделать, например, с помощью обработки текста (функции регулярных выражений), но зачем делать это именно так?   -  person mathguy    schedule 30.04.2017
comment
Я мог бы сделать это, объединив значения следующим образом: REPLACE(TRIM(extractvalue(xmltype(xml_input), '/Element[1]/id') || ' ' || .. || extractvalue(xmltype(xml_input), '/Element [n]/id')) , ' ', ',') но максимальное количество элементов - n - может варьироваться. Я хотел бы уменьшить обработку Oracle, чтобы получить запрос быстрее. Спасибо   -  person Luca Di Gregorio    schedule 01.05.2017


Ответы (2)


Если вам нужно, вы можете сделать что-то вроде того, что я показываю в запросе ниже.

Однако я не считаю это хорошим подходом; то, что вы делаете сейчас, создавая XML-таблицу, а затем используя LISTAGG, кажется лучше.

with inputs ( xml_str ) as (
       select '...
               <Element><id>first</id></Element>
               <Element><id>second</id></Element>
               ...'
       from dual
     )
-- End of test data (not part of the solution); SQL query begins below this line
select rtrim( regexp_replace( xml_str, '.*?(<Element><id>(.*?)</id></Element>|$)'
                            , '\2,', 1, 0, 'n')
            , ',') as id_list
from   inputs
;



ID_LIST
------------
first,second
person mathguy    schedule 30.04.2017
comment
Спасибо, ввод xml, который я привел в качестве примера, не является фактическим xml для прецессии, поэтому я думаю, что regexp_replace станет более сложным. - person Luca Di Gregorio; 01.05.2017
comment
@LucaDiGregorio - согласен! Делайте то, что вы делаете прямо сейчас, или то, что предложил Алекс. - person mathguy; 01.05.2017

Вы можете сделать это прямо в функции XMLDB с помощью XPath string-join function< /а>.

Как XMLQuery, если у вас есть одно значение для обработки (с вашими примерами данных, предоставленными через CTE и фиктивный корневой узел):

with t (xml) as (
  select xmltype('<root>
<Element><id>first</id></Element>
<Element><id>second</id></Element>
</root>') from dual
)
select xmlquery('
  for $i in /root
    return <e>{ fn:string-join($i/Element/id, ",") }</e>/text()'
  passing xml
  returning content
) as result
from t;

RESULT                                                                          
--------------------------------------------------------------------------------
first,second

Или с XMLTable:

with t (xml) as (
  select xmltype('<root>
<Element><id>first</id></Element>
<Element><id>second</id></Element>
</root>') from dual
)
select x.*
from t
cross join xmltable('
  for $i in /root
    return <e>{ fn:string-join($i/Element/id, ",") }</e>'
  passing xml
  columns result varchar2(4000) path '.'
) x;

RESULT                                                                          
--------------------------------------------------------------------------------
first,second

Я не уверен, что это имеет большое преимущество перед listagg(), но может быть интересно сравнить производительность обоих с вашими реальными данными, особенно если узлов много. За исключением того, что, изменив тип столбца XMLTable на CLOB, вы можете получить большее значение из этой версии, чем вы можете с listagg().

person Alex Poole    schedule 01.05.2017
comment
Большое спасибо. Для своей цели я буду использовать что-то вроде этого: выберите x.RESULT из xmltable(' for $i в /root return ‹dummy›{ fn:string-join($i/Element/id, ,) }‹/dummy›' передача xmltype('‹root› ‹Element›‹id›first‹/id›‹/Element› ‹Element›‹id›second‹/id›‹/Element› ‹/root› ') столбцов результат varchar2 (4000) путь '/ манекен' ) х; - person Luca Di Gregorio; 01.05.2017