Actually you're close to your goal, you just need to use nodes() method to split your rows and then get values:

select
    s.SqmId,
    m.c.value('@id', 'varchar(max)') as id,
    m.c.value('@type', 'varchar(max)') as type,
    m.c.value('@unit', 'varchar(max)') as unit,
    m.c.value('@sum', 'varchar(max)') as [sum],
    m.c.value('@count', 'varchar(max)') as [count],
    m.c.value('@minValue', 'varchar(max)') as minValue,
    m.c.value('@maxValue', 'varchar(max)') as maxValue,
    m.c.value('.', 'nvarchar(max)') as Value,
    m.c.value('(text())[1]', 'nvarchar(max)') as Value2
from sqm as s
    outer apply s.data.nodes('Sqm/Metrics/Metric') as m(c)

sql fiddle demo

Answer from roman on Stack Overflow
🌐
Barney Lawrence
barneylawrence.com › 2021 › 03 › 23 › querying-xml-in-sql-server-part-2-simple-paths-and-attributes-with-the-value-method
Querying XML In SQL Server – Part 2 – Simple Paths and Attributes With the Value Method
April 5, 2021 - With MyNumbers AS ( SELECT N FROM (VALUES(1),(2),(3),(4),(5),(6),(7)) AS N(N) ) SELECT MyNumbers.N, X.MyXML.value( '(/items/item/poll/ results[sql:column("MyNumbers.N")]/ result[@value = "Best"]/ @numvotes)[1]' ,'int' ) AS BestPollVotes FROM #MyXML AS X CROSS JOIN MyNumbers ORDER BY N; The row for N=7 gives a NULL because there is no 7th Element, SQL Server doesn’t know this though and will take a look for you anyway. Don’t go wild with the above. If you want to split repeating XML elements into rows of a table there’s a better way.
Top answer
1 of 4
137

Actually you're close to your goal, you just need to use nodes() method to split your rows and then get values:

select
    s.SqmId,
    m.c.value('@id', 'varchar(max)') as id,
    m.c.value('@type', 'varchar(max)') as type,
    m.c.value('@unit', 'varchar(max)') as unit,
    m.c.value('@sum', 'varchar(max)') as [sum],
    m.c.value('@count', 'varchar(max)') as [count],
    m.c.value('@minValue', 'varchar(max)') as minValue,
    m.c.value('@maxValue', 'varchar(max)') as maxValue,
    m.c.value('.', 'nvarchar(max)') as Value,
    m.c.value('(text())[1]', 'nvarchar(max)') as Value2
from sqm as s
    outer apply s.data.nodes('Sqm/Metrics/Metric') as m(c)

sql fiddle demo

2 of 4
16

I've been trying to do something very similar but not using the nodes. However, my xml structure is a little different.

You have it like this:

<Metrics>
    <Metric id="TransactionCleanupThread.RefundOldTrans" type="timer" ...>

If it were like this instead:

<Metrics>
    <Metric>
        <id>TransactionCleanupThread.RefundOldTrans</id>
        <type>timer</type>
        .
        .
        .

Then you could simply use this SQL statement.

SELECT
    Sqm.SqmId,
    Data.value('(/Sqm/Metrics/Metric/id)[1]', 'varchar(max)') as id,
    Data.value('(/Sqm/Metrics/Metric/type)[1]', 'varchar(max)') AS type,
    Data.value('(/Sqm/Metrics/Metric/unit)[1]', 'varchar(max)') AS unit,
    Data.value('(/Sqm/Metrics/Metric/sum)[1]', 'varchar(max)') AS sum,
    Data.value('(/Sqm/Metrics/Metric/count)[1]', 'varchar(max)') AS count,
    Data.value('(/Sqm/Metrics/Metric/minValue)[1]', 'varchar(max)') AS minValue,
    Data.value('(/Sqm/Metrics/Metric/maxValue)[1]', 'varchar(max)') AS maxValue,
    Data.value('(/Sqm/Metrics/Metric/stdDeviation)[1]', 'varchar(max)') AS stdDeviation,
FROM Sqm

To me this is much less confusing than using the outer apply or cross apply.

I hope this helps someone else looking for a simpler solution!

🌐
Red Gate Software
red-gate.com › home › the xml methods in sql server
The XML Methods in SQL Server | Simple Talk
August 16, 2021 - The value() method makes it easier to work with individual values in order to compare or combine them, either with other XML values or values of different types. As the following syntax illustrates, when you call the value() method, you must specify the XQuery expression and the Transact-SQL data type for the returned data:
🌐
Microsoft Learn
learn.microsoft.com › en-us › sql › relational-databases › xml › for-xml-sql-server
FOR XML (SQL Server) - SQL Server | Microsoft Learn
September 3, 2025 - Learn how to retrieve formal results of a SQL query as XML by specifying the FOR XML clause in the query.
🌐
T-SQL Tutorial
tsql.info › xml › value.php
SQL Server XML value() method
The SQL Server XML value() method is a powerful XML function that allows you to extract a specific value from an XML document. This method returns a single scalar value of a specified data type from an XML instance.
Top answer
1 of 9
243
select
  Roles
from
  MyTable
where
  Roles.value('(/root/role)[1]', 'varchar(max)') like 'StringToSearchFor'

In case your column is not XML, you need to convert it. You can also use other syntax to query certain attributes of your XML data. Here is an example...

Let's suppose that data column has this:

<Utilities.CodeSystems.CodeSystemCodes iid="107" CodeSystem="2" Code="0001F" CodeTags="-19-"..../>

... and you only want the ones where CodeSystem = 2 then your query will be:

select 
  [data] 
from
  [dbo].[CodeSystemCodes_data]
  
where
  CAST([data] as XML).value('(/Utilities.CodeSystems.CodeSystemCodes/@CodeSystem)[1]', 'varchar(max)') = '2'

These pages will show you more about how to query XML in T-SQL:

Querying XML fields using t-sql

Flattening XML Data in SQL Server

EDIT

After playing with it a little bit more, I ended up with this amazing query that uses CROSS APPLY. This one will search every row (role) for the value you put in your like expression...

Given this table structure:

create table MyTable (Roles XML)

insert into MyTable values
('<root>
   <role>Alpha</role>
   <role>Gamma</role>
   <role>Beta</role>
</root>')

We can query it like this:

select * from 

(select 
       pref.value('(text())[1]', 'varchar(32)') as RoleName
from 
       MyTable CROSS APPLY

       Roles.nodes('/root/role') AS Roles(pref)
)  as Result

where RoleName like '%ga%'

You can check the SQL Fiddle here: http://sqlfiddle.com/#!18/dc4d2/1/0

2 of 9
41
declare @T table(Roles xml)

insert into @T values
('<root>
   <role>Alpha</role>
   <role>Beta</role>
   <role>Gamma</role>
</root>')

declare @Role varchar(10)

set @Role = 'Beta'

select Roles
from @T
where Roles.exist('/root/role/text()[. = sql:variable("@Role")]') = 1

If you want the query to work as where col like '%Beta%' you can use contains

declare @T table(Roles xml)

insert into @T values
('<root>
   <role>Alpha</role>
   <role>Beta</role>
   <role>Gamma</role>
</root>')

declare @Role varchar(10)

set @Role = 'et'

select Roles
from @T
where Roles.exist('/root/role/text()[contains(., sql:variable("@Role"))]') = 1
🌐
Benjamin's Blog
sqlbenjamin.wordpress.com › 2019 › 09 › 13 › sql-tip-the-query-and-value-methods-xml
SQL Tip: The “query()” and “value()” methods (XML)
February 16, 2022 - As I mentioned above, if you know XQuery then you have a lot more options available to you in using this method; there are a number of XQuery functions that can be used in conjunction with this method to get what you want out of xml fragments. For example, in the “ReportServer” (SSRS) database there’s a column in a table which contains information about subscriptions. Here is an example of this column: <ParameterValues> <ParameterValue> <Name>TO</Name> <Value>someone@microsoft.com</Value> </ParameterValue> <ParameterValue> <Name>IncludeReport</Name> <Value>True</Value> </ParameterValue>
Find elsewhere
🌐
Microsoft Learn
learn.microsoft.com › en-us › sql › relational-databases › xml › use-the-value-and-nodes-methods-with-openxml
Use the value() and nodes() Methods with OPENXML - SQL Server | Microsoft Learn
Applies to: SQL Server Azure SQL Database Azure SQL Managed Instance SQL database in Microsoft Fabric · You can use multiple value() methods on xml data type in a SELECT clause to generate a rowset of extracted values.
🌐
SQLServerCentral
sqlservercentral.com › blogs › sql-querying-xml-attributes-from-xml-column
SQL- Querying XML attributes from XML Column – SQLServerCentral
January 26, 2015 - create table #demo (field1 xml) insert into #demo (field1) values ('<dimensions> <dimension name="height" value="0.14" /> <dimension name="width" value="12.77"/> </dimensions>') SELECT x.v.value('@name[1]', 'VARCHAR(100)') AS dimtype , x.v.value('@value[1]', 'VARCHAR(100)') AS dimvalue from #demo cross apply field1.nodes('/dimensions/dimension[@name = "height" or @name = "width"]') x(v) Using SQL Where Clause to fetch dimension type data ‘HEIGHT’ or ‘WIDTH’
🌐
ScholarHat
scholarhat.com › home
SQL Server XML Data Type
September 18, 2025 - XML data type was introduced in SQL Server 2005 to work with XML data. Using this data type, we can store XML in its native format and can also query/modify the XML data within the xml. We can use xml data types like: ... We can define xml data type field to NOT NULL or we can provide a default value ...
🌐
MSSQLTips
mssqltips.com › home › basic sql server xml querying
Basic SQL Server XML Querying
February 20, 2013 - We’ve assumed too much – that the value we are looking for in the Education node will exactly match the text ‘Bachelors’. If we look at the sample Demographics XML fragment closely, we see that there is a space after the word. So, if we modify our query and run it again, we get the following: --filtering XML data using XQuery - adding a space to the string ;WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/IndividualSurvey' AS ns) SELECT COUNT(1) FROM Person.Person WHERE Demographics.exist('(/ns:IndividualSurvey/ns:Education[.="Bachelors "])')=1 GO
🌐
MSSQLTips
mssqltips.com › home › working with xml data in sql server
Working with XML data in SQL Server
September 10, 2024 - I introduced two ways to retrieve <Contact> data, the method value using singleton, for the first [1] and the second [2] occurrences, which is ok if we can ensure that all phone data will come first; otherwise, it is better to query the PAT alias for the correct value using the contact attribute @Type. Observe that the attribute ClinicId of <Patients> is at the root element. Once the XPATH expression starts in the path Patients/Patient to read the record, we use the notation “../”, which means one node above. In some cases, if you need to go further, use “../../” for two nodes above, a
🌐
IBM
ibm.com › docs › ssw_ibm_i_74 › rzasp › rzasp_xmlvalues.htm
XML values in SQL
In terms of the XQuery/XPath 2.0 data model, DB2 for i SQL defines an XML value as a sequence that contains a single document node, with a sub-tree containing the document's content. Representing an XML value as a document node guarantees that the value can be serialized to a character ...
🌐
IBM
ibm.com › docs › en › db2 › 11.1.0
Lesson 6: Querying XML data
This lesson shows you how to query XML data by using SQL, XQuery (with XQuery expressions), or a combination of both.
🌐
SQL Shack
sqlshack.com › filtering-xml-columns-using-xquery-in-sql-server
Filtering XML Columns using XQuery in SQL Server
February 6, 2020 - The tabular format from an XML ... with the help of XQuery. XQuery will return the XML node value in the tabular model with a combination of nodes() and value() function on the XML column....
🌐
ArcGIS
desktop.arcgis.com › en › arcmap › latest › manage-data › using-sql-with-gdbs › xml-column-queries.htm
XML column queries - ArcMap Resources for ArcGIS Desktop
SQL developers can use database XML functions to retrieve specific values from item definitions using XPath (a query language for XML documents).
🌐
Red Gate Software
red-gate.com › home › effective strategies for storing and parsing xml in sql server
Store, Parse & Convert XML in SQL Server | Simple Talk
June 3, 2024 - SQL Server provides a native XML data type with built-in support for XQuery, OPENXML, XML indexes, and schema validation. Store XML in XML-typed columns (not VARCHAR) to get type checking, XML-specific indexing, and native XQuery support. Parse XML values using the .value(), .query(), .nodes(), and .modify() methods, or use OPENXML for rowset-based shredding.
Top answer
1 of 2
5

I believe the reason you're getting these results is because the namespace for the xml is not the same as in the select statements. I've altered your example to briefly explain each case.

declare @xmlMessage xml

set @xmlMessage ='<pmsg:Messages xmlns:pmsg="pmsg">
    <pmsg:Message Info="blah"/>
    <pmsg:Message Info="blah"/>
</pmsg:Messages>'

;WITH XMLNAMESPACES('pmsg' as pmsg)
SELECT msgs.msg.value('@Info', 'nvarchar(max)') FROM @xmlMessage.nodes('pmsg:Messages/pmsg:Message') msgs(msg); 
-- return twos rows 'blah'

The nodes() expression provides a set of two pmsg:Message nodes, and the select reads the value of @Info from each row.

;WITH XMLNAMESPACES('pmsg' as pmsg)
SELECT @xmlMessage.value('(/pmsg:Messages/pmsg:Message/@Info)[1]', 'nvarchar(max)'); 
--Returns a single rowset of one row containing 'blah'

This will only return a single row 'blah' because the [1] in the query restricts to the first item found. Actually in this form the [1] is required. SQL will throw exeception of you dont include it becuase the expression must evaluate to a single value.

;WITH XMLNAMESPACES('pmsg' as pmsg)
SELECT t.x.value('(/pmsg:Messages/pmsg:Message/@Info)[1]', 'nvarchar(max)') as INFO FROM @xmlMessage.nodes('/*') t(x);
--Returns a rowset of one row containing 'blah'

Like the previous example, the xpath query evaluates to a single item and the nodes() expresson provides a row set on the top level node:

2 of 2
4

Try these statements:

DECLARE @xmlMessage XML = '<pmsg:Messages xmlns:pmsg="someUrl.com">
    <pmsg:Message Info="blah"/>
</pmsg:Messages>'

;WITH XMLNAMESPACES('someUrl.com' as pmsg)
SELECT msgs.msg.value('@Info', 'nvarchar(max)') FROM @xmlMessage.nodes('pmsg:Messages/pmsg:Message') msgs(msg); -- returns 0 rows

WITH XMLNAMESPACES('someUrl.com' as pmsg)
SELECT @xmlMessage.value('(/pmsg:Messages/pmsg:Message/@Info)[1]', 'nvarchar(max)'); -- returns NULL

WITH XMLNAMESPACES('someUrl.com' as pmsg)
SELECT t.x.value('(/pmsg:Messages/pmsg:Message/@Info)[1]', 'nvarchar(max)') as INFO FROM @xmlMessage.nodes('/*') t(x); -- returns NULL

Your sample XML is not valid XML so I have corrected it, assuming the stored proc does pass back valid XML. XML Namespaces are usually URLs, so I have provided a sample one - change the URL to your URL to make it work with your code. Your second statement does not work because it references the messages element twice ie messsages/messages, where the inner element is called message. You also have some semi-colons in the wrong place. Once corrected each of these three statements brought back a result:

learn more about XML namespaces in SQL Server here:

Adding Namespaces Using WITH XMLNAMESPACES
http://msdn.microsoft.com/en-us/library/ms177400.aspx