3

This is an extension to the question XPath: Select first element with a specific attribute and pertains specifically to Oracle.

Presuming we have the following data in a column called xml_data:

<bookstore> <category> <book location="CAN">A1</book> <book location="FIN">A2</book> </category> <category> <book location="FIN">B1</book> <book location="US">B2</book> </category> <category> <book location="US">C2</book> <book location="FIN">C1</book> </category> </bookstore> 

I'm trying to select the first occurrence of a book under category with attribute location="US". For the above data, that should return B2.

The suggested solution, (/bookstore/category/book[@location='US'])[1], does not work.
It returns the following error: ORA-19110: unsupported XQuery expression
Here's a SQL Fiddle demonstrating the issue.

How can I adapt the solution to work with Oracle's idea of XPaths?

0

2 Answers 2

4

It's a shame that Oracle does not support this kind of path expression. You should consider filing a bug report.

You could use a predicate that checks whether there are any preceding:: book elements (in document order) with the same attribute value. In fact, I'm almost inclined to say that this is the only XPath-only approach that works.

SELECT parsed.book FROM xml_test x, XMLTABLE( '/bookstore' PASSING XMLTYPE(x.xml_data) COLUMNS "BOOK" VARCHAR2(16) PATH '/bookstore/category/book[@location="US"][not(preceding::book[@location = "US"])]' ) parsed ; 

which would yield the wanted result:

BOOK - B2 
Sign up to request clarification or add additional context in comments.

2 Comments

Ahh, I was so close and you beat me to it. Do you think preceeding:: axis would be better performance wise than the method I came up with using count?
@DanField Your solution only works because the first book element with @location="US" accidentally is the second child of its parent element. Try changing the input XML and see what happens. If both US books are the first child of their parents, your expression returns nothing.
1

Tricky way with no XQuery (just for fun):

select extract(extract(xml_data,'/bookstore/category/book[@location="US"]'),'/*[1]') from ... 

Solution with XQuery works good with expression from referenced question:

select XMLQuery( '($xdata/bookstore/category/book[@location="US"])[1]' passing xml_data as "xdata" returning content ) from ... 

For me original expression works well with extract on SQL Fiddle too:

SQLFiddle example

Problem with original query is that expression must be placed in XQuery itself, not in field extract path. Expression is Ok:

SELECT parsed.book FROM xml_test x, XMLTABLE( '(/bookstore/category/book[@location="US"])[1]' PASSING XMLTYPE(x.xml_data) COLUMNS "BOOK" VARCHAR2(16) PATH '/book' ) parsed ; 

Updated:

  1. removed sentence about XPath 1.0 / 2.0 difference as source of a problem because expression works with extract() too.

  2. original solution from referenced question verified to work with extract() on SQLFiddle.

  3. Added right variant for original query

4 Comments

Interesting. There is no XPath 1.1 though, only XPath 1.0, 2.0 and 3.0. Does XMLQuery really mean XQuery, or does it mean evaluating a path expression? Also, I'm not sure what you mean by "Solution with XQuery works good with expression fom referenced question because" - expressions along the lines of (...)[1] also work in XPath 1.0.
XMLQuery is a one of Oracle functions to work with XML and invokes XQuery on XMLType values.
Odd, why does the the (path)[1] notation work in an XMLQuery but not an XMLTable?
Sorry for all commenters, answer updated: please review new variant. Specially thanks for @MathiasMüller for raising right questions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.