Oracle SQL UPDATEXML 替换节点值,即使它是空的
Oracle SQL UPDATEXML Replace node value even if it's empty
我有一个 CLOB 数据库列,其中包含一个大 XML : XML_CONF
我通常使用函数 updateXML 来修改 XML 的特定节点,效果很好。但是今天,我遇到了很多麻烦,因为我要修改的节点有时是空的,在这种情况下不起作用...
具有空文本值的 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>
你可以这样做:
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
与默认值相同(来自评论):
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 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
与默认值不同)。出于某种原因,在 db<>fiddle 和 SQL Fiddle 上都得到 "ORA-19112: error raised during evaluation: XQuery Update connot be compiled",它们都是 11.20.02;但在我的 11.2.0.4 和 12.2.0.1 数据库上运行良好。
您可以添加检查是否存在相关节点以避免不必要的更新。
我有一个 CLOB 数据库列,其中包含一个大 XML : XML_CONF 我通常使用函数 updateXML 来修改 XML 的特定节点,效果很好。但是今天,我遇到了很多麻烦,因为我要修改的节点有时是空的,在这种情况下不起作用...
具有空文本值的 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>
你可以这样做:
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
与默认值相同(来自评论):
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 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
与默认值不同)。出于某种原因,在 db<>fiddle 和 SQL Fiddle 上都得到 "ORA-19112: error raised during evaluation: XQuery Update connot be compiled",它们都是 11.20.02;但在我的 11.2.0.4 和 12.2.0.1 数据库上运行良好。
您可以添加检查是否存在相关节点以避免不必要的更新。