【问题标题】:Oracle SQL UPDATEXML Replace node value even if it's emptyOracle SQL UPDATEXML 替换节点值,即使它是空的
【发布时间】:2018-12-20 22:21:20
【问题描述】:

我有一个包含大 XML 的 CLOB 数据库列:XML_CONF 我通常使用函数 updateXML 来修改 XML 的特定节点,效果很好。但是今天遇到了很多麻烦,因为我要修改的节点有时候是空的,这种情况下就不行了……

带有空 textValue 的 XML 示例:

<ns2:ConfigurationTree xmlns:ns2="com.xxxx" xmlns="com.xxxx.yyyy">
<ns2:ConfigurableProduct ...>...
    <ns2:CPNode name="cpX">...
        <ns2:FormProperty name="fpX">
            <ns2:SingleValuation isCommentUserChoice="false" isValueUserChoice="false" isQtyUserChoice="false">
                <ns2:Value>
                    **<ns2:textValue/>**
                </ns2:Value>
            </ns2:SingleValuation>
        </ns2:FormProperty>
    </ns2:CPNode>
</ns2:ConfigurableProduct>

带有 textValue 的 XML 示例:

<ns2:ConfigurationTree xmlns:ns2="com.xxxx" xmlns="com.xxxx.yyyy">
<ns2:ConfigurableProduct ...>...
    <ns2:CPNode name="cpX">...
        <ns2:FormProperty name="fpX">
            <ns2:SingleValuation isCommentUserChoice="false" isValueUserChoice="false" isQtyUserChoice="false">
                <ns2:Value>
                    **<ns2:textValue>123456</ns2:textValue>**
                </ns2:Value>
            </ns2:SingleValuation>
        </ns2:FormProperty>
    </ns2:CPNode>
</ns2:ConfigurableProduct>

例如,将 textValue 内容替换为“78910”,我尝试这样处理这两种情况:

update T_TABLE set XML_CONF = updatexml(xmltype(XML_CONF),
'//FormProperty[@name="fpX"]//Value/textValue',xmltype('<textValue>78910</textValue>'),
'//FormProperty[@name="fpX"]//Value/textValue/text()','78910',
'xmlns:ns2="com.xxxx" xmlns="xxxx.yyyy"').getClobVal();

但结果破坏了 XML(节点中不再有前缀和 xmlns 为空):

<ns2:ConfigurationTree xmlns:ns2="com.xxxx" xmlns="com.xxxx.yyyy">
<ns2:ConfigurableProduct ...>...
    <ns2:CPNode name="cpX">...
        <ns2:FormProperty name="fpX">
            <ns2:SingleValuation isCommentUserChoice="false" isValueUserChoice="false" isQtyUserChoice="false">
                <ns2:Value>
                    **<textValue xmlns="">78910</textValue>**
                </ns2:Value>
            </ns2:SingleValuation>
        </ns2:FormProperty>
    </ns2:CPNode>
</ns2:ConfigurableProduct>

如果我记得相同的请求,但使用不同的 textValue,之后它不会再更新任何内容...我认为这是因为节点上的前缀被破坏...

我尝试用 XMLQuery (Oracle 12) 来做,但这是同样的问题。

编辑

它几乎适用于:

    update T_TABLE set XML_CONF = updatexml(xmltype(XML_CONF),
  '//FormProperty[@name="fpX"]//Value/textValue/text()','78910',
  '//FormProperty[@name="fpX"]//Value/textValue[not(text())]',xmltype('<textValue>78910</textValue>'),
  xmlns:ns2="com.xxxx" xmlns="xxxx.yyyy"' ).getClobVal();

但在输出中我没有新的 ns2:textValue 节点,我只有:

<ns2:Value><textValue xmlns="">78910</textValue></ns2:Value>

为什么会破坏 ns2 前缀,为什么会放一个空的 xmlns 属性?

如果我在新节点中指定命名空间,它可以工作,但它似乎没用,因为它们已经在根节点中声明:

    update T_TABLE set XML_CONF = updatexml(xmltype(XML_CONF),
  '//FormProperty[@name="fpX"]//Value/textValue/text()','78910',
  '//FormProperty[@name="fpX"]//Value/textValue[not(text())]',xmltype('<ns2:textValue xmlns:ns2="com.xxxx" xmlns="xxxx.yyyy">78910</ns2:textValue>'),
  'xmlns:ns2="com.xxxx" xmlns="xxxx.yyyy"' ).getClobVal();

给:

<ns2:textValue xmlns:ns2="com.xxxx" xmlns="xxxx.yyyy">78910</ns2:textValue></ns2:Value>

【问题讨论】:

    标签: sql xml oracle xpath updatexml


    【解决方案1】:

    你可以这样做:

    update T_TABLE set XML_CONF = updatexml(xmltype(XML_CONF),
      '//ns2:FormProperty[@name="fpX"]//ns2:Value/ns2:textValue/text()','78910',
      '//ns2:FormProperty[@name="fpX"]//ns2:Value/ns2:textValue[not(text())]',
         xmlelement("ns2:textValue", xmlattributes('com.xxxx' as "xmlns:ns2"), '78910'),
      'xmlns:ns2="com.xxxx" xmlns="com.xxxx.yyyy"').getClobVal();
    

    识别文本节点或没有文本的节点;或者如果ns2 与默认值相同(来自 cmets):

    update T_TABLE set XML_CONF = updatexml(xmltype(XML_CONF),
      '//FormProperty[@name="fpX"]//Value/textValue/text()','78910',
      '//FormProperty[@name="fpX"]//Value/textValue[not(text())]',
         xmlelement("ns2:textValue", xmlattributes('com.xxxx' as "xmlns:ns2"), '78910'),
      'xmlns:ns2="com.xxxx" xmlns="com.xxxx"').getClobVal();
    

    db<>fiddle 使用您的真实命名空间。新创建的textValue 节点重新声明了ns2,但在功能上这无关紧要。

    当然,updateXML 在 12c 中已被弃用,但您应该能够使用 Xquery update 做同样的事情。事实上,这更简单:

    update t_table set xml_conf = xmlquery(
       'copy $d := .
        modify (
          for $i in $d//*:FormProperty[@name="fpX"]//*:Value/*:textValue
            return replace value of node $i with $newValue
        )
        return $d'
        passing xmltype(xml_conf), '78910' as "newValue"
        returning content
      ).getClobVal();
    

    为了简单起见,我对命名空间进行了通配符(实际上,我还没有弄清楚如何使它与命名空间前缀一起工作,即使ns2 与默认值不同)。出于某种原因,在 dbfiddle 和 SQL Fiddle(均为 11.20.02)上出现“ORA-19112:评估期间引发错误:XQuery Update connot becompiled”;但在我的 11.2.0.4 和 12.2.0.1 数据库上运行良好。

    您可以为现有的相关节点添加检查以避免不必要的更新。

    【讨论】:

    • 奇怪,它不起作用:/两次请求之后,XML仍然是空的:
    • fpCodeActiviteArty
    • 它适用于您的样本。我不知道您在做什么不同,我看不到原始 XML 或您正在执行的实际更新。也许您的 ns2 定义不完全匹配或其他什么,这会默默地什么都不做。但这显然只是一个猜测。
    • 我在这里修改了fiddle以放置整个XML,但是字符串太长了:dbfiddle.uk/…我不能修改的formProperty的名称是:name="fpCodeActiviteArty" 另外,我只是看到即使您的解决方案有效,在 textValue 节点为空的情况下,它也不会保留 XXX,而是直接输入值“78910”,例如 78910
    • 事实上,如果我只是将我的示例中的命名空间的真正声明放在你的小提琴中,它就不再起作用了:dbfiddle.uk/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多