【问题标题】:Update value of a node, ignore if not exists更新节点的值,不存在则忽略
【发布时间】:2019-07-08 13:50:53
【问题描述】:

我想替换 XML 中节点的值。 XML 存储在 Oracle 12.2 数据库中的 XMLTYPE 列中。 我的 XML:

<Warehouse>
  <WarehouseId>1</WarehouseId>
  <WarehouseName>Southlake, Texas</WarehouseName>
  <Building>Owned</Building>
  <Area>25000</Area>
</Warehouse>

UPDATEXML 函数可以完成这项工作,但速度很慢。

select
UPDATEXML(myxmlcolumn, '/Warehouse/Building/text()','mynewvalue')
from mytable;

Oracle 说 UPDATEXML 是 deprecated,而应该使用 XMLQUERY。 所以,我尝试了 XMLQUERY:

select
 XMLQUERY(
'copy $t := $x modify(
  replace value of node $t/Warehouse/Building with "mynewvalue"
) return $t'
from mytable;

它工作快得多,但有一个小问题:如果请求的节点不存在,它会失败并显示 XVM-01155:[XUDY0027] Invalid target expression

例如,此选择失败并出现上述错误(注意 ZZZ 假节点名称):

select
 XMLQUERY(
'copy $t := $x modify(
  replace value of node $t/Warehouse/ZZZ with "mynewvalue"
) return $t'
from mytable;

问题: 如何更改代码以忽略不存在的节点?

【问题讨论】:

    标签: oracle xmltype


    【解决方案1】:

    IF-ELSE 声明可能会有所帮助:) 检查示例。

        with mytable as (select xmltype('<Warehouse>
          <WarehouseId>1</WarehouseId>
          <WarehouseName>Southlake, Texas</WarehouseName>
          <Building>Owned</Building>
          <Area>25000</Area>
        </Warehouse>') myxmlcolumn from dual) 
        select 
    
         XMLQUERY(
        'copy $t := . modify(
         if( $t/Warehouse/WarehouseName) then  
          (
            replace value of node $t/Warehouse/WarehouseName with "mynewvalue"
          )
          else ()
        ) return $t' passing myxmlcolumn returning content)
    
        from mytable
         union all 
         select 
    
         XMLQUERY(
        'copy $t := . modify(
         if( $t/Warehouse/ZZZZ) then  
          (
            replace value of node $t/Warehouse/ZZZZ with "mynewvalue"
          )
          else ()
        ) return $t' passing myxmlcolumn returning content)
    
        from mytable
    union all
    select 
     XMLQUERY(
    'copy $t := . modify(
     for $node in  $t/Warehouse/ZZZZ
      return replace value of node $node with "mynewvalue"
    ) return $t' passing myxmlcolumn returning content) from mytable;
    

    【讨论】:

    • 这个方案需要读表两次,效率不高
    • 我又添加了一个使用 for 循环方法的示例。 @Michael 我在哪里读表两次?
    • 我误解了“union all”的用法——正如我现在所看到的,它只是为了解释。真正的代码应该只包含 3 个选项之一。 “for 循环”的解决方案是这里最好的解决方案,因为它会自动处理不存在的节点。
    【解决方案2】:

    根据@Arkadiusz Łukasiewicz 的出色回答,我编制了完整的解决方案,其中包括:

    • 忽略不存在的节点
    • 能够在单个 xmlquery 调用中更改多个节点

    这里是:

    select
        xmlquery(
    '    copy $t := $x modify
            (
              (for $i in $t/Warehouse/Building
                 return replace value of node $i with "aaa"),
              (for $i in $t/Warehouse/ZZZ
                 return replace value of node $i with "bbb)
            )   
        return $t 
    '
    passing 
        XMLRECORD as "x"  
    from mytable
    

    【讨论】: