Reputation: 255
I have this query:
select XMLMetadata from taObjectMetadata where ObjectMetadataTypeId = 1
which returns 3000+ rows, each row containing XML:
<objectMetaData>
<fileLocation fileName="CM63951.mxf06092018233409;21.png" />
</objectMetaData>
I need to pull that fileName out. I can do this for any given row easily enough with XQuery:
declare @x XML = ('<objectMetaData>
<fileLocation fileName="CM63951.mxf06092018233409;21.png" />
</objectMetaData>')
select x.value(N'@fileName', N'nvarchar(100)') as Filename
from @x.nodes(N'/objectMetaData/fileLocation') t(x)
Which gives me exactly the bit I need. However, I need this for every set of this XML in the table. Attempting to put the query in the declaration XML fails because it of course returns multiple results.
Do I need to use a WHILE loop here. or is there a better/more elegant way to do it?
Upvotes: 1
Views: 66
Reputation: 67321
Your question does not make clear, whether the XML is always like the one you show (just one <fileLocation>
) or if this structure might contain more.
If it's just one the question might be: Why XML?
Anyhow: This is a mockup-scenario for both cases:
DECLARE @mockup TABLE(ID INT IDENTITY,TheXml XML);
INSERT INTO @mockup VALUES
('<objectMetaData>
<fileLocation fileName="CM63951.mxf06092018233409;21.png" />
</objectMetaData>')
,('<objectMetaData>
<fileLocation fileName="OneMore.png" />
</objectMetaData>')
,('<objectMetaData>
<fileLocation fileName="TheFirst.png" />
<fileLocation fileName="TheSecond.png" />
</objectMetaData>');
You can pick the first directly. No derived table needed
--Returns the only (or the first) file name
SELECT m.ID
,TheXml.value('(/objectMetaData/fileLocation/@fileName)[1]','nvarchar(max)')
FROM @mockup m
--If there are many <fileLocation>
-nodes, we need the derived table via .nodes()
--Returns multiple filenames
SELECT m.ID
,fl.value('@fileName','nvarchar(max)')
FROM @mockup m
CROSS APPLY TheXml.nodes('/objectMetaData/fileLocation') A(fl);
So for your actual query this should be either this
--for one filename per row
select XMLMetadata.value('(/objectMetaData/fileLocation/@fileName)[1]','nvarchar(max)')
from taObjectMetadata
where ObjectMetadataTypeId = 1
or this
--for many filenames per row
select fl.value('@fileName','nvarchar(max)')
from taObjectMetadata
cross apply XMLMetadata.nodes('/objectMetaData/fileLocation') A(fl)
where ObjectMetadataTypeId = 1
Upvotes: 1
Reputation: 46249
You can try to use CROSS APPLY
select x.value('@fileName', N'nvarchar(100)') as Filename
FROM yourTable CROSS APPLY yourTable.data.nodes(N'objectMetaData/fileLocation') as t(x)
NOTE : yourTable
could instead your current result set.
Your query might be like this.
select x.value('@fileName', N'nvarchar(100)') as Filename
FROM (
select XMLMetadata
from taObjectMetadata
where ObjectMetadataTypeId = 1
) t1 CROSS APPLY t1.XMLMetadata.nodes(N'objectMetaData/fileLocation') as t(x)
Here is a sample : sqlfiddle
Upvotes: 1