XML query() works, value() requires singleton found xdt:untypedAtomic
Asked Answered
F

3

36

I have a typed xml document stored as text. So I use CONVERT the data type to xml by using a Common Table Expression in order to be able to use XML methods:

WITH xoutput AS (
  SELECT CONVERT(xml, t.requestpayload) 'requestpayload'
    FROM TABLE t
   WHERE t.methodid = 1)
SELECT x.requestpayload.query('declare namespace s="http://blah.ca/api";/s:validate-student-request/s:student-id') as studentid
  FROM xoutput x

Query works, returning to me the element. But I'm only interested in the value:

WITH xoutput AS (
  SELECT CONVERT(xml, t.requestpayload) 'requestpayload'
    FROM TABLE t
   WHERE t.methodid = 1)
SELECT x.requestpayload.value('declare namespace s="http://blah.ca/api";/s:validate-student-request/s:student-id', 'int') as studentid
  FROM xoutput x

This gives me the following error:

'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'

What I've googled says that the XPATH/XQUERY needs to be inside parenthesis and/or needs "[1]" - neither has worked. There's only one student-id element in the xml, though I guess the schema allows for more?

Additionally, there are numerous element values I'd like to retrieve - is there a way to declare the namespace once rather than per method call?

Forswear answered 19/8, 2009 at 19:15 Comment(0)
L
71

You need to use this:

SELECT 
        x.requestpayload.value('declare namespace s="http://blah.ca/api";
            (/s:validate-student-request/s:student-id)[1]', 'int') 
    AS
        studentid
    FROM 
        xoutput x

You need to put your XPath in ( ... ) and add a [1] to simply select the first value of that sequence.

Laraelaraine answered 19/8, 2009 at 19:37 Comment(1)
Great answer, this code run fast and is elegant. +1General
B
8

I believe this might also do:

SELECT 
   x.requestpayload.query('declare namespace s="http://blah.ca/api";
                           /s:validate-student-request/s:student-id').value('.', 'int') 
  as studentid
FROM xoutput x
Brauer answered 22/8, 2011 at 18:0 Comment(0)
A
4

For those interested in performance I ran a query to compare these approaches and the first option with "() and add a [1]" was MUCH faster than ".query('strFranchise').value('.',...)".

Difference in Execution plan was 15% to 85% when running one after the other on same data. So ()[1] is over 5 times faster! Execution plan is much different.

Adulterous answered 13/5, 2015 at 6:37 Comment(1)
As I understand it [1] only returns the first result while .value('.' returns all results.Spam

© 2022 - 2024 — McMap. All rights reserved.