• Quick thought on optimization, as Alan mentioned, the text() method is faster.

    😎

    Same XML data sample as before

    USE tempdb;

    GO

    SET NOCOUNT ON;

    DECLARE @xml XML;

    SET @xml =

    '<?xml version="1.0" encoding="UTF-8"?>

    <result resultID="200">

    <status>OK</status>

    <message></message>

    <countryCode>US</countryCode>

    <zoneName>America/Los_Angeles</zoneName>

    <abbreviation>PDT</abbreviation>

    <gmtOffset>-25200</gmtOffset>

    <dst>1</dst>

    <timestamp>1430141082</timestamp>

    </result>'

    Looking at the first nodes query without the text method

    SELECT

    Tbl.Col.value('(countryCode)[1]' , 'NVARCHAR(5)' ) AS countryCode,

    Tbl.Col.value('(zoneName)[1]' , 'NVARCHAR(50)') AS zoneName,

    Tbl.Col.value('(abbreviation)[1]' , 'NVARCHAR(6)' ) AS abbreviation,

    Tbl.Col.value('(gmtOffset)[1]' , 'INT' ) AS gmtOffset,

    Tbl.Col.value('(dst)[1]' , 'INT' ) AS dst

    FROM @xml.nodes('result') Tbl(Col);

    Execution Plan 1

    we see two XML Reader functon calls for each value joined with a nested loop. Changing the query to use the text() method removes all but one of the extra functon calls. This reduces the cost of the query by approximately 75%.

    SELECT

    Col.value('(countryCode/text())[1]' , 'NVARCHAR(5)' ) AS countryCode,

    Col.value('(zoneName/text())[1]' , 'NVARCHAR(50)') AS zoneName,

    Col.value('(abbreviation/text())[1]' , 'NVARCHAR(6)' ) AS abbreviation,

    Col.value('(gmtOffset/text())[1]' , 'INT' ) AS gmtOffset,

    Col.value('(dst/text())[1]' , 'INT' ) AS dst,

    Col.value('(@resultID)' , 'INT' ) AS resultID

    FROM @xml.nodes('/result') Tbl(Col);

    Execution Plan 2

    Looking at the second execution plan we see there is still some room for improvements, the retrieval of the attribute @resultID is close to 50% of the cost, there is still an extra XML Reader function call and each value has a Filter operator which isn't really doing anything.

    The last version of the query bypasses the nodes() method by directly calling the value() method on the XML variable.

    SELECT

    @xml.value('(/result/countryCode/text())[1]' , 'NVARCHAR(5)' ) AS countryCode,

    @xml.value('(/result/zoneName/text())[1]' , 'NVARCHAR(50)') AS zoneName,

    @xml.value('(/result/abbreviation/text())[1]' , 'NVARCHAR(6)' ) AS abbreviation,

    @xml.value('(/result/gmtOffset/text())[1]' , 'INT' ) AS gmtOffset,

    @xml.value('(/result/dst/text())[1]' , 'INT' ) AS dst,

    @xml.value('(/result/@resultID)[1]' , 'INT' ) AS resultID

    ;

    Execution Plan 3

    Not much more to do here 😉

    Comparing the costs of the three queries we see that the improvements are substantial, the third being close to 1% of the first.