Search code examples
sqlxmlxqueryxsql

XQuery SQL Extract text value from a child element of a specific node


I'm struggling with the following situation: I need to extract that '10000' after

...name="stateid"> <from>10000</from>

I've managed to extract some that I need from the first node audittrail

SELECT ref.value ('@datetime', 'nvarchar(364)') as [LastTimeActioned],
ref.value ('@changedby', 'nvarchar(364)') as [Actioned_By]
FROM Audit 
CROSS APPLY audit.Xml.nodes ('/audittrail') R(ref)


<audittrail id="137943" datetime="29-Feb-2016 15:42:06" changedby="quality" type="update">
   <fields>
     <field id="10022" name="Content Control">
      <mergedValue>dsad</mergedValue>
      <from />
      <to>dsad</to>
     </field>
     <field id="10027" name="Document Controller">
      <mergedValue>quality</mergedValue>
      <from />
      <to>quality</to>
    </field>
    <field id="10028" name="Document Owner">
      <mergedValue>quality</mergedValue>
      <from />
      <to>quality</to>
    </field>
    <field id="10029" name="Document Type">
      <mergedValue>Contract/Agreement</mergedValue>
      <from />
      <to>Contract/Agreement</to>
    </field>
    <field id="10067" name="StateId">
      <from>10000</from>
      <to>10000</to>
    </field>
    ....
  </fields>
</audittrail>

Solution

  • You're looking for the Xpath:

     (fields/field[@name="StateId"]/from)[1]
    

    i.e. Find me the "fields/field" element with the attribute name of value StateId, and select the contents of the immediate child from element:

    SELECT 
       ref.value ('@datetime', 'nvarchar(364)') as [LastTimeActioned],
       ref.value ('@changedby', 'nvarchar(364)') as [Actioned_By],
       ref.value ('(fields/field[@name="StateId"]/from)[1]', 'integer') as StateIdFrom
    FROM Audit 
    CROSS APPLY audit.Xml.nodes ('/audittrail') R(ref)
    

    SqlFiddle Here

    Note that you need to explicitly select the first text element result in XQuery (hence the extra parenthesis() and [1])

    More technically correctly, you could also restrict the selection to the text() of the child from, i.e.:

    (fields/field[@name="StateId"]/from/text())[1]