首页 > 解决方案 > 在 Oracle 中使用多个命名空间更新 CLOB 列

问题描述

我正在尝试更新 oracle DB 中的 clob 列内的元素。

我面临的第一个挑战是我的 clob xml 有 2 个命名空间,我无法让它工作。

<?xml version="1.0"?>
<esbmsg:EsbMessage xmlns:esbmsg="http://www.test.com/esb/message/1.0">
  <esbmsg:Body>
    <Transaction xmlns="http://test.com">
      <test-element>
        <finalElement>false</finalElement>
      </test-elemen>
    </Transaction>
  </esbmsg:Body>
</esbmsg:EsbMessage>



select x.* from cc_messagehistory y
cross join xmltable(
  xmlnamespaces('http://www.test.com/esb/message/1.0' as "esbmsg",
    'http://test.com ' ),
  '/esbmsg:EsbMessage'
  passing xmltype.createxml(y.payload)  
    factext varchar2(10) path '/esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement'
) x;

ORA-19102:XQuery 字符串文字预期为 19102。00000 - “XQuery 字符串文字预期” *原因:包含 XQuery 表达式的字符串文字丢失。*操作:将 XQuery 表达式指定为字符串文字。行错误:64 列:99

标签: oracle

解决方案


ORA-01902 的直接原因是您错过了default关键字:

  xmlnamespaces('http://www.test.com/esb/message/1.0' as "esbmsg",
    default 'http://test.com'),

我已经删除了 URI 末尾的多余空格,这会在以后引起问题。但是您也缺少columns关键字,您可以简化 CLOB 值到 XMLType 的转换。

将它们放在一起,并使用 CTE 提供您的(更正后的)示例 XML:

-- CTE for sample data
with cc_messagehistory(payload) as (
select to_clob('<?xml version="1.0"?>
<esbmsg:EsbMessage xmlns:esbmsg="http://www.test.com/esb/message/1.0">
  <esbmsg:Body>
    <Transaction xmlns="http://test.com">
      <test-element>
        <finalElement>false</finalElement>
      </test-element>
    </Transaction>
  </esbmsg:Body>
</esbmsg:EsbMessage>') from dual
)
-- actual query
select x.*
from cc_messagehistory y
cross join xmltable (
  xmlnamespaces (
    'http://www.test.com/esb/message/1.0' as "esbmsg",
    default 'http://test.com'
  ),
  '/esbmsg:EsbMessage'
  passing xmltype(y.payload)  
  columns factext varchar2(10)
  path '/esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement'
) x;

FACTEXT   
----------
false

对于更新,您可以执行以下操作:

update cc_messagehistory y
set payload = XMLSerialize(document
  XMLQuery('declare default element namespace "http://test.com"; (: :)
    declare namespace esbmsg="http://www.test.com/esb/message/1.0"; (: :)
    copy $i := $xml modify (
      for $j in $i//esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement
      return replace value of node $j with $new
    )
    return $i'
    passing xmltype(y.payload) as "xml",
    'true' AS "new"
    returning content
  )
  indent size=2
)
where xmlexists('declare default element namespace "http://test.com"; (: :)
  declare namespace esbmsg="http://www.test.com/esb/message/1.0"; (: :)
  $xml//esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement[text()="false"]'
  passing xmltype(y.payload) as "xml");

它将源 CLOB 转换为:

<?xml version="1.0"?>
<esbmsg:EsbMessage xmlns:esbmsg="http://www.test.com/esb/message/1.0">
  <esbmsg:Body>
    <Transaction xmlns="http://test.com">
      <test-element>
        <finalElement>true</finalElement>
      </test-element>
    </Transaction>
  </esbmsg:Body>
</esbmsg:EsbMessage>

db<>fiddle(在 18c 上工作;在 11gR2 上出现错误,但补丁级别可能会有所不同;也在 12cR1 的其他地方成功测试过)


推荐阅读