Skip to content
Advertisement

Extracting multiple values from BLOB as XML

I have an XML like this in a BLOB column:

<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
   <Event>
      <year>1983</year>
      <Country>
         <Location>Lisbon</Location>
         <Type>Political</Type>
      </Country>
   </Event>
   <Event>
      <Country>
         <Location>USA</Location>
         <Type>Entertainment</Type>
         <year>2016</year>
      </Country>
   </Event>
</history>
</document>

As you can see the year can be either in the event level or at country level. There can be multiple events and multiple countries per event. This whole XML is stored in a BLOB column in Oracle. I need to extract the value of the year or better check if the year is 2000 and if so return the primary key of the row.

I used EXISTSNODE to check if the year tag is present.

select pk from table where XMLType(blobdata, nls_charset_id('UTF8')).EXISTSNODE('/Document/history/Event[*]/year',
'xmlns="urn:xyzns"') = 1 and EXTRACTVALUE(XMLTYPE(blobdata, nls_charset_id('UTF8')), '/Document/history/Event[*]/year/text()',
'xmlns="urn:xyzns"') = '2000';

However this fails and the extractvalue query returns multiple nodes, so I changed the parameter to '/Document/history/Event[1]/year/text()' to check and it works. However this wouldnt be enough as it only checks the first event tag.

I looked at other questions here and one of the options was to use XMLTABLE since extractvalue is deprecated. I am having trouble understanding the parameters given inside the XMLTABLE. Could someone explain how to use XMLTABLE in this scenario? I should point out that the original datatype is BLOB and not CLOB. Thank you.

Advertisement

Answer

Use XMLTABLE to get values for both locations and then use COALESCE to show whichever is not NULL:

SELECT COALESCE( year, country_year ) AS year,
       location,
       type
FROM   table_name t
       CROSS APPLY XMLTABLE(
         XMLNAMESPACES( DEFAULT 'urn:xyzns' ),
         '/document/history/Event'
         PASSING XMLTYPE(t.blobdata, nls_charset_id('UTF8'))
         COLUMNS
           year         NUMBER(4,0)   PATH './year',
           country_year NUMBER(4,0)   PATH './Country/year',
           location     VARCHAR2(200) PATH './Country/Location',
           type         VARCHAR2(200) PATH './Country/Type'
       ) x

Which, for the sample data:

CREATE TABLE table_name ( blobdata BLOB );

INSERT INTO table_name
  VALUES (
    UTL_RAW.CAST_TO_RAW(
'<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
   <Event>
      <year>1983</year>
      <Country>
         <Location>Lisbon</Location>
         <Type>Political</Type>
      </Country>
   </Event>
   <Event>
      <Country>
         <Location>USA</Location>
         <Type>Entertainment</Type>
         <year>2016</year>
      </Country>
   </Event>
</history>
</document>'
    )
  );

Outputs:

YEAR | LOCATION | TYPE         
---: | :------- | :------------
1983 | Lisbon   | Political    
2016 | USA      | Entertainment

db<>fiddle here

6 People found this is helpful
Advertisement