Oracle查询以重命名XMLTYPE内的标签。

huangapple go评论68阅读模式
英文:

Oracle query to rename tags inside XMLTYPE

问题

以下是翻译好的部分:

我有一个带有XMLType字段(PROPERTIES)的表(Table1),还有另一个带有2个字段的表(Table2):

  • TAG_NAME
  • TAG_RENAME

每个XMLType记录可能具有不同的标签名称。
我需要在选择的输出中获取具有重新命名标签的XMLType字段(我不需要更新字段)。

这里是一个示例:

Table1:

ID PROPERTIES
1 </Fields><TAG1>123</TAG1><TAG2>345</TAG2></Fields>
2 </Fields><TAG1>999</TAG1><TAG3>XXX</TAG3></Fields>

Table2:

TAG_NAME TAG_RENAME
TAG1 NEW_TAG1
TAG2 NEW_TAG2
TAG3 NEW_TAG3

所需的输出:

ID NEW_PROPERTIES
1 </Fields><NEW_TAG1>123</NEW_TAG1><NEW_TAG2>345</NEW_TAG2></Fields>
3 </Fields><NEW_TAG1>999</NEW_TAG1><NEW_TAG3>XXX</NEW_TAG3></Fields>

我尝试使用XQuery这种方式(左连接,因为一些PROPERTIES字段可能为NULL):

select t.ID, t.tag_name, t.value FROM Table1 t
left join  XMLTable ('for $i in fn:collection("oradb:/MY_USER/TABLE2"), $x in //descendant::*
where $i/ROW/TAG_NAME=local-name($x)
return element r {
           element tag_name  {$i/ROW/TAG_RENAME/text()}
        , element tag_value {$x/text()}
         }'
passing case when t.properties is null then null else xmltype ('<?xml version="1.0" encoding="iso-8859-1" ?>'||t.properties)end
columns tag_name varchar2(30) path 'tag_name',
                tag_value varchar2(30) path 'tag_value'                
)x on 1=1 and x.tag_value is not null;

作为输出,我得到了:

ID TAG_NAME TAG_VALUE
1 NEW_TAG1 123
1 NEW_TAG2 345
2 NEW_TAG1 999
2 NEW_TAG3 XXX

所以它几乎可以工作。问题是我想要每个ID在单独的一行上。我尝试使用listagg,但速度很慢。有没有一种直接在XQuery内部重命名标签的方法?

英文:

I have a table (Table1) with an XMLType field (PROPERTIES) and I have another table (Table2) with 2 fields:

  • TAG_NAME
  • TAG_RENAME

Each XMLType record may have different tag names.
I need to get as output of select, the XMLType field with tags renamed (I don't need to update field).

Here an example:

Table1:

ID PROPERTIES
1 &lt;/Fields&gt;&lt;TAG1&gt;123&lt;/TAG1&gt;&lt;TAG2&gt;345&lt;/TAG2&gt;&lt;/Fields&gt;
2 &lt;/Fields&gt;&lt;TAG1&gt;999&lt;/TAG1&gt;&lt;TAG3&gt;XXX&lt;/TAG3&gt;&lt;/Fields&gt;

Table2:

TAG_NAME TAG_RENAME
TAG1 NEW_TAG1
TAG2 NEW_TAG2
TAG3 NEW_TAG3

Output needed:

ID NEW_PROPERTIES
1 &lt;/Fields&gt;&lt;NEW_TAG1&gt;123&lt;/NEW_TAG1&gt;&lt;NEW_TAG2&gt;345&lt;/NEW_TAG2&gt;&lt;/Fields&gt;
3 &lt;/Fields&gt;&lt;NEW_TAG1&gt;999&lt;/NEW_TAG1&gt;&lt;NEW_TAG3&gt;XXX&lt;/NEW_TAG3&gt;&lt;/Fields&gt;

I tried with XQuery in this way (left join because some PROPERTIES fields can be NULL):

select t.ID, t.tag_name, t.value FROM Table1 t
left join  XMLTable (&#39;for $i in fn:collection(&quot;oradb:/MY_USER/TABLE2&quot;), $x in //descendant::*
where $i/ROW/TAG_NAME=local-name($x)
return element r {
           element tag_name  {$i/ROW/TAG_RENAME/text()}
        , element tag_value {$x/text()}
         }&#39;
passing case when t.properties is null then null else xmltype (&#39;&lt;?xml version=&quot;1.0&quot; encoding=&quot;iso-8859-1&quot; ?&gt;&#39;||t.properties)end
columns tag_name varchar2(30) path &#39;tag_name&#39;,
                tag_value varchar2(30) path &#39;tag_value&#39;                
)x on 1=1 and x.tag_value is not null;

As output I got

ID TAG_NAME TAG_VALUE
1 NEW_TAG1 123
1 NEW_TAG2 345
2 NEW_TAG1 999
2 NEW_TAG3 XXX

So it is almost working. The problem is that I'd like to have each ID on a single line. I tried with listagg and it's working but it's slow. Is there a way to rename TAGs directly inside XQuery?

答案1

得分: 1

你可以使用XQuery来修改XML。

create table sample (id, val) as
  select 1, '&lt;Fields&gt;&lt;TAG1&gt;123&lt;/TAG1&gt;&lt;TAG2&gt;345&lt;/TAG2&gt;&lt;/Fields&gt;' from dual
  union all
  select 2, '&lt;Fields&gt;&lt;TAG1&gt;999&lt;/TAG1&gt;&lt;TAG3&gt;XXX&lt;/TAG3&gt;&lt;/Fields&gt;' from dual
create table renaming(tag, renamed) as
  select 'TAG1','NEW_TAG1' from dual union all
  select 'TAG2','NEW_TAG2' from dual union all
  select 'TAG3','NEW_TAG3' from dual
select
  s.*
  , xmlserialize(document xmlquery(
    'copy $res := $x modify (
      for $r in $ren/ROWSET/ROW
      for $i in $res/Fields/*[name(.) = $r/TAG]
      return rename node $i as $r/RENAMED
    ) return $res'
    passing
      xmltype(s.val) as "x",
      /*Mapping XML*/
      dbms_xmlgen.getxmltype('select * from renaming') as "ren"
    returning content
  ) as clob) as renamed
from sample s
ID VAL RENAMED
1 <Fields><NEW_TAG1>123</NEW_TAG1><NEW_TAG2>345</NEW_TAG2></Fields> <Fields><NEW_TAG1>123</NEW_TAG1><NEW_TAG2>345</NEW_TAG2></Fields>
2 <Fields><NEW_TAG1>999</NEW_TAG1><NEW_TAG3>XXX</NEW_TAG3></Fields> <Fields><NEW_TAG1>999</NEW_TAG1><NEW_TAG3>XXX</NEW_TAG3></Fields>

fiddle

UPD: 如果您想要删除不匹配的标签或没有值的标签,您可以使用多个修改。

select
  s.*
  , xmlserialize(document xmlquery(
    'copy $res := $x modify (
      (
        for $i in $res/Fields/*
        return (
          delete node $i[not($ren/ROWSET/ROW/TAG[text() = name($i)])],
          delete node $i[not(text())]
        )
      ),
      (
        for $renaming in $ren/ROWSET/ROW
        for $i in $res/Fields/*[name(.) = $renaming/TAG]
        return rename node $i as $renaming/RENAMED/text()
      )
    ) return $res'
    passing
      xmltype(s.val) as "x",
      /*Mapping XML*/
      dbms_xmlgen.getxmltype('select * from renaming') as "ren"
    returning content
  ) as clob) as renamed
from sample s

对于这个示例数据,返回结果如下:

create table sample (id, val) as
  select 1, '&lt;Fields&gt;&lt;TAG1&gt;123&lt;/TAG1&gt;&lt;TAG2&gt;345&lt;/TAG2&gt;&lt;TAG_WITH_NO_VALUE/&gt;&lt;/Fields&gt;' from dual
  union all
  select 2, '&lt;Fields&gt;&lt;TAG1&gt;999&lt;/TAG1&gt;&lt;TAG3&gt;XXX&lt;/TAG3&gt;&lt;/Fields&gt;' from dual
create table renaming(tag, renamed) as
  select 'TAG_WITH_NO_VALUE','TAG_WITH_NO_VALUE__2' from dual union all
  select 'TAG2','NEW_TAG2' from dual union all
  select 'TAG3','NEW_TAG3' from dual

返回结果如下:

ID VAL RENAMED
1 <Fields><NEW_TAG2>345</NEW_TAG2></Fields> <Fields><NEW_TAG2>345</NEW_TAG2></Fields>
2 <Fields><NEW_TAG3>XXX</NEW_TAG3></Fields> <Fields><NEW_TAG3>XXX</NEW_TAG3></Fields>

fiddle

英文:

You may use XQuery to modify XML.

create table sample (id, val) as
  select 1, &#39;&lt;Fields&gt;&lt;TAG1&gt;123&lt;/TAG1&gt;&lt;TAG2&gt;345&lt;/TAG2&gt;&lt;/Fields&gt;&#39; from dual
  union all
  select 2, &#39;&lt;Fields&gt;&lt;TAG1&gt;999&lt;/TAG1&gt;&lt;TAG3&gt;XXX&lt;/TAG3&gt;&lt;/Fields&gt;&#39; from dual

create table renaming(tag, renamed) as
  select &#39;TAG1&#39;,&#39;NEW_TAG1&#39; from dual union all
  select &#39;TAG2&#39;,&#39;NEW_TAG2&#39; from dual union all
  select &#39;TAG3&#39;,&#39;NEW_TAG3&#39; from dual

select
  s.*
  , xmlserialize(document xmlquery(
    &#39;copy $res := $x modify (
      for $r in $ren/ROWSET/ROW
      for $i in $res/Fields/*[name(.) = $r/TAG]
      return rename node $i as $r/RENAMED
    ) return $res&#39;
    passing
      xmltype(s.val) as &quot;x&quot;,
      /*Mapping XML*/
      dbms_xmlgen.getxmltype(&#39;select * from renaming&#39;) as &quot;ren&quot;
    returning content
  ) as clob) as renamed
from sample s

ID VAL RENAMED
1 &lt;Fields>&lt;TAG1>123&lt;/TAG1>&lt;TAG2>345&lt;/TAG2>&lt;/Fields> &lt;Fields>&lt;NEW_TAG1>123&lt;/NEW_TAG1>&lt;NEW_TAG2>345&lt;/NEW_TAG2>&lt;/Fields>
2 &lt;Fields>&lt;TAG1>999&lt;/TAG1>&lt;TAG3>XXX&lt;/TAG3>&lt;/Fields> &lt;Fields>&lt;NEW_TAG1>999&lt;/NEW_TAG1>&lt;NEW_TAG3>XXX&lt;/NEW_TAG3>&lt;/Fields>

fiddle

UPD: If you want to remove unmatched tags or tags with no value you may use multiple modifications.

select
  s.*
  , xmlserialize(document xmlquery(
    &#39;copy $res := $x modify (
      (
        for $i in $res/Fields/*
        return (
          delete node $i[not($ren/ROWSET/ROW/TAG[text() = name($i)])],
          delete node $i[not(text())]
        )
      ),
      (
        for $renaming in $ren/ROWSET/ROW
        for $i in $res/Fields/*[name(.) = $renaming/TAG]
        return rename node $i as $renaming/RENAMED/text()
      )
    ) return $res&#39;
    passing
      xmltype(s.val) as &quot;x&quot;,
      /*Mapping XML*/
      dbms_xmlgen.getxmltype(&#39;select * from renaming&#39;) as &quot;ren&quot;
    returning content
  ) as clob) as renamed
from sample s

which for this sample data

create table sample (id, val) as
  select 1, &#39;&lt;Fields&gt;&lt;TAG1&gt;123&lt;/TAG1&gt;&lt;TAG2&gt;345&lt;/TAG2&gt;&lt;TAG_WITH_NO_VALUE/&gt;&lt;/Fields&gt;&#39; from dual
  union all
  select 2, &#39;&lt;Fields&gt;&lt;TAG1&gt;999&lt;/TAG1&gt;&lt;TAG3&gt;XXX&lt;/TAG3&gt;&lt;/Fields&gt;&#39; from dual

create table renaming(tag, renamed) as
  select &#39;TAG_WITH_NO_VALUE&#39;,&#39;TAG_WITH_NO_VALUE__2&#39; from dual union all
  select &#39;TAG2&#39;,&#39;NEW_TAG2&#39; from dual union all
  select &#39;TAG3&#39;,&#39;NEW_TAG3&#39; from dual

returns

ID VAL RENAMED
1 &lt;Fields>&lt;TAG1>123&lt;/TAG1>&lt;TAG2>345&lt;/TAG2>&lt;TAG_WITH_NO_VALUE/>&lt;/Fields> &lt;Fields>&lt;NEW_TAG2>345&lt;/NEW_TAG2>&lt;/Fields>
2 &lt;Fields>&lt;TAG1>999&lt;/TAG1>&lt;TAG3>XXX&lt;/TAG3>&lt;/Fields> &lt;Fields>&lt;NEW_TAG3>XXX&lt;/NEW_TAG3>&lt;/Fields>

fiddle

答案2

得分: 0

您可以通过生成XSLT来完成:

with data(xml) as (
    select xmltype(q'{<Fields><TAG1>123</TAG1><TAG2>345</TAG2></Fields>}') from dual union all
    select xmltype(q'{<Fields><TAG1>999</TAG1><TAG3>XXX</TAG3></Fields>}') from dual -- union all
),
mapping(oldtag, newtag) as (
    select 'TAG1', 'NEW_TAG1' from dual union all
    select 'TAG2', 'NEW_TAG2' from dual union all
    select 'TAG3', 'NEW_TAG3' from dual -- union all
),
xslt(xsl) as (
    select q'{<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<Fields>}' || listagg(tr,chr(10)) within group(order by rn)
    || '</Fields>' || q'</xsl:template>
</xsl:stylesheet>}' as xlst from (
        select rownum as rn, '<xsl:if test="/Fields/' || oldtag || '">' 
            || '<' || newtag || '><xsl:value-of select="/Fields/' || oldtag || '"/></' || newtag || '>'
            || '</xsl:if>' 
            as tr
        from mapping
    )
)
select xmltransform(xml,xsl)
from data, xslt
;

注意:这是您提供的SQL查询的中文翻译。

英文:

You could do it by generating a XSLT:

with data(xml) as (
	select xmltype(q&#39;{&lt;Fields&gt;&lt;TAG1&gt;123&lt;/TAG1&gt;&lt;TAG2&gt;345&lt;/TAG2&gt;&lt;/Fields&gt;}&#39;) from dual union all
	select xmltype(q&#39;{&lt;Fields&gt;&lt;TAG1&gt;999&lt;/TAG1&gt;&lt;TAG3&gt;XXX&lt;/TAG3&gt;&lt;/Fields&gt;}&#39;) from dual -- union all
),
mapping(oldtag, newtag) as (
	select &#39;TAG1&#39;, &#39;NEW_TAG1&#39; from dual union all
	select &#39;TAG2&#39;, &#39;NEW_TAG2&#39; from dual union all
	select &#39;TAG3&#39;, &#39;NEW_TAG3&#39; from dual -- union all
),
xslt(xsl) as (
	select q&#39;{&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
	&lt;xsl:stylesheet xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot; version=&quot;1.0&quot;&gt;
	&lt;xsl:template match=&quot;/&quot;&gt;
	&lt;Fields&gt;}&#39; || listagg(tr,chr(10)) within group(order by rn)
	|| &#39;&lt;/Fields&gt;&#39; || q&#39;{&lt;/xsl:template&gt;
	&lt;/xsl:stylesheet&gt;}&#39; as xlst from (
		select rownum as rn, &#39;&lt;xsl:if test=&quot;/Fields/&#39; || oldtag || &#39;&quot;&gt;&#39; 
			|| &#39;&lt;&#39; || newtag || &#39;&gt;&lt;xsl:value-of select=&quot;/Fields/&#39; || oldtag || &#39;&quot;/&gt;&lt;/&#39; || newtag || &#39;&gt;&#39; 
			|| &#39;&lt;/xsl:if&gt;&#39; 
			as tr
		from mapping
	)
)
select xmltransform(xml,xsl)
from data, xslt
;

huangapple
  • 本文由 发表于 2023年5月29日 20:42:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76357486.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定