1

I have an NCLOB column on my database which has a structure like this:

<Storage xmlns="http://tempuri.org/Storage.xsd">
  <TableInfo>
    <Name>Genotypes</Name> <------------------ I DON'T want to get this
    ...
  </TableInfo>
  <ColumnInfo>
    <ID>1</ID>
    <Name>cre</Name> <------------------------ I want to get this
    ...
  </ColumnInfo>
  <ColumnInfo>
    <ID>2</ID>
    <Name>Tph2</Name> <----------------------- I want to get this
    ...
  </ColumnInfo>
  <ColumnInfo>
    <ID>3</ID>
    <Name>New_Field</Name> <------------------ I want to get this
    ...
  </ColumnInfo>
  ...
</Storage>

I need to get all the values under the /Storage/ColumnInfo/Name path and I've been trying this query (based on the answer on this question) but I'm getting a blank result.

Attempted Solution

SELECT ver.class_version, x.tag
FROM class_version ver
  LEFT JOIN XMLTABLE('/Storage/ColumnInfo'
    PASSING SYS.DBMS_XMLGEN.GETXMLTYPE(
      'SELECT t.dictionary_data
      FROM (
        SELECT v.class_def_id, v.class_version, v.dictionary_data, MAX(v.row_created) OVER (PARTITION BY v.class_def_id) AS max_date
        FROM class_version v
      ) t'
    )
    COLUMNS tag VARCHAR(256) PATH 'Name'
  ) x ON (1=1)
WHERE ver.class_def_id = 2672
  AND ver.class_version = '1.0.0.4'

Desired Output

class_version       tag
-------------------------------------------------------------
1.0.0.4             cre
1.0.0.4             Tph2
1.0.0.4             New_Field

Actual Output

class_version       tag
-------------------------------------------------------------
1.0.0.4             NULL

Table Structure

It may help if I provide the structure of the table so here it is:

id    class_def_id   class_version     dictionary_data (NCLOB)
------------------------------------------------------------------------
1000  2672           1.0.0.0           NULL
1001  2672           1.0.0.1           -- XML (as per above) --
1002  2672           1.0.0.2           -- XML (as per above) --
1003  2672           1.0.0.3           -- XML (as per above) --
1004  2672           1.0.0.4           -- XML (as per above) --
2001  1234           1.0.0.0           NULL
2002  1234           1.0.0.1           -- XML (as per above) --
2003  1234           1.0.0.2           -- XML (as per above) --

Basically I'm trying to get the values of all the /Storage/ColumnInfo/Name nodes in the dictionary_data column for each class_def_id - from the latest version that is.

Updated Query

WITH markup AS (
  SELECT *
  FROM (
    SELECT v.class_def_id, v.class_version, XMLTYPE(v.dictionary_data) AS xmldata, v.row_created,
      MAX(v.row_created) OVER (PARTITION BY v.class_def_id) AS max_date
    FROM class_version v
    WHERE v.dictionary_data IS NOT NULL
  ) q
  WHERE q.row_created = q.max_date
)

SELECT x.tag
FROM markup m
  CROSS JOIN XMLTABLE(
    XMLNAMESPACES(DEFAULT 'http://tempuri.org/Storage.xsd'),
    'Storage/ColumnInfo'
    PASSING m.xmldata
    COLUMNS tag VARCHAR2(11) PATH 'Name'
  ) x
Community
  • 1
  • 1
dokgu
  • 4,957
  • 3
  • 39
  • 77

1 Answers1

1

but I'm getting a blank result.

This is because there is a default namespace. Use xmlnamespaces() function to specify a namespace (in this case default one).

with 
  t1 (xmldata) as(
    select xmltype(to_nclob('
<Storage xmlns="http://tempuri.org/Storage.xsd">
  <TableInfo>
    <Name>Genotypes</Name> 
  </TableInfo>
  <ColumnInfo>
    <ID>1</ID>
    <Name>cre</Name> 
  </ColumnInfo>
  <ColumnInfo>
    <ID>2</ID>
    <Name>Tph2</Name>
  </ColumnInfo>
  <ColumnInfo>
    <ID>3</ID>
    <Name>New_Field</Name> 
  </ColumnInfo>
</Storage>')) from dual
  )
 select q.colname
   from t1  t
        cross join xmltable(xmlnamespaces(default 'http://tempuri.org/Storage.xsd'),
                            'Storage/ColumnInfo'
                             passing t.xmldata
                             columns ColName  varchar2(11)  path 'Name'
                           ) q

Result:

COLNAME    
-----------
cre        
Tph2       
New_Field  

3 rows selected.
Nick Krasnov
  • 26,886
  • 6
  • 61
  • 78
  • Thanks this gets me closer to what I need. Within the `WITH` clause I just added `WHERE t1.class_def_id = 2672 AND t1.class_version = '1.0.0.4'` to make this work ([PasteBin](http://pastebin.com/7Mdjv4AY)) but I'm hoping not to do any filtering in there as this is meant to be a view. When I remove those filters I get an error saying XML parsing failed and that `expected '=' instead of '>'`. – dokgu Jan 25 '17 at 19:00
  • There are some rows in the database where the `NCLOB` column is `NULL` so I'm guessing that's causing the problems? I tried using `WHERE t1.dictionary_data IS NOT NULL` but it didn't fix the problem. – dokgu Jan 25 '17 at 19:15
  • @uom-pgregorio You posted only abridged version of an xml. Now you are asking in a comment about something that is not in the question, what do you expect? I have no idea what is going on there apart from what I saw in your question. Edit your question and give us a more complete picture. DDL of the table with all relevant columns and sample data, your code, which is as you are saying generating an error. – Nick Krasnov Jan 25 '17 at 19:18
  • Updated my question. Hopefully those are enough? Let me know if you need more info. – dokgu Jan 25 '17 at 19:30
  • 1
    @uom-pgregorio `NULL` should not be a problem. More likely there is a not well formed XML somewhere among those rows. As one of the examples the error you are talking about will be raised if there is an attribute without a value, something like `Val`. So you need to be sure that XML a well formed. – Nick Krasnov Jan 25 '17 at 19:39
  • Oh I see, it's kinda hard for me to check all of those one by one. I'll look for ways to get rows with malformed XML then. Thanks! – dokgu Jan 25 '17 at 19:43