Search code examples
oracle-databaseoracle11g

Update XML value in stored in Clob


Does anyone know a solution how I can change a specific value within xml? XML is stored in Clob datatype.

My XML looks like:

<settings name="TEST_NAME" path="TEST_PATH">
<values>
    <value param="Version">20200207</value>
</values>
<collections>
    <collection name="Items">
        <values>
            <value param="TEST_PARAM">true</value>
        </values>
        <collections>
            </collection>
            <collection name="TEST_COL">
                <values>
                    <value param="DockedLeft">0</value>
                </values>
                <collections>
                    <collection name="ItemLink0">
                        <values>
                            <value param="ItemName">TEST_PARAM</value>
                        </values>
                    </collection>
                </collections>
            </collection>
    </collection>
</collections>

What I need to update is "TEST_PARAM" inside TEST_COL collection. Collection name itemlink0 can be different. Thanks for the answers!


Solution

  • I created some sample XML that is valid and inserted into a CLOB column. It should suffice to show how this can be done using XMLQuery and a SQL update.

    CREATE table xml_tbl (xml_str CLOB)
    
    INSERT INTO xml_tbl VALUES(
    '<settings name="TEST_NAME" path="TEST_PATH">
      <collections>
        <collection name="TEST_COL">
          <values>
            <value param="DockedLeft">0</value>
          </values>
          <collections>
            <collection name="ItemLink0">
              <values>
                <value param="ItemName">TEST_PARAM</value>
              </values>
            </collection>
          </collections>
        </collection>
      </collections>
    </settings>')
    

    The element value can be updated by converting the CLOB to/from XMLType and utilizing XMLQuery to update the XML. The element having the dynamic attribute name can be a PL/SQL variable within the PASSING clause of the XMLQuery.

    DECLARE
      l_dyn_attr_name VARCHAR2(100):= 'ItemLink0';
      l_element_value VARCHAR2(100):= 'new value';
    BEGIN
    
      UPDATE xml_tbl xt
      SET    xt.xml_str = 
         XMLTYPE.GETCLOBVAL(XMLQuery('copy $i := $x1 modify
                         (for $j in $i/settings/collections/collection[@name="TEST_COL"]/collections/collection[@name=$dynamic_attr_name]/values/value
                          return replace value of node $j with $new_elem_value)
                          return $i' PASSING XMLTYPE(xt.xml_str) AS "x1"
                                            ,l_dyn_attr_name AS "dynamic_attr_name"
                                            ,l_element_value AS "new_elem_value" RETURNING CONTENT));
    
      COMMIT;
    END;