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 OverflowVideos
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
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!
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
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
Given that the XML field is named 'xmlField'...
SELECT
[xmlField].value('(/person//firstName/node())[1]', 'nvarchar(max)') as FirstName,
[xmlField].value('(/person//lastName/node())[1]', 'nvarchar(max)') as LastName
FROM [myTable]
Considering that XML data comes from a table 'table' and is stored in a column 'field': use the XML methods, extract values with xml.value(), project nodes with xml.nodes(), use CROSS APPLY to join:
SELECT
p.value('(./firstName)[1]', 'VARCHAR(8000)') AS firstName,
p.value('(./lastName)[1]', 'VARCHAR(8000)') AS lastName
FROM table
CROSS APPLY field.nodes('/person') t(p)
You can ditch the nodes() and cross apply if each field contains exactly one element 'person'. If the XML is a variable you select FROM @variable.nodes(...) and you don't need the cross apply.
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:
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