Search code examples
sqlxmlloopsreporting-servicesvarcharmax

Getting Multiple Answers out of an XML String SQL


I have an XML document saved in a Column as varchar(max). The text I want is surrounded by <Text> Words I Want</Text>but these Text tags repeat sometimes 4 or 5 times.

How do I loop through the same document x number of times dependent on the number of text tags?

Currently I'm using this to pull out the first bit of text

 DECLARE @first_char nvarchar(10)
DECLARE @second_char nvarchar(10)

SET @first_char = 'xt>';
SET @second_char = '</text>';

SELECT[TestId]
      ,[SectionId],
SUBSTRING
(
-- column
 settings
-- start position
,CHARINDEX(@first_char, Settings , 1) + 3
-- length
,CASE
WHEN (CHARINDEX(@second_char, Settings , 0) - CHARINDEX(@first_char, Settings, 0)) > 0
THEN CHARINDEX(@second_char, Settings, 0) - CHARINDEX(@first_char, Settings, 0) - 3
ELSE 0
END
) AS Response
FROM [B2IK-TestBuilder].[dbo].[Questions] 
group by [TestId]
      ,[SectionId], settings

and I know how many times the Text tag appears.

This is an example of the xml document saved a varchar(max):

<Settings>
  <ShowNotes>true</ShowNotes>
  <ShowComment>false</ShowComment>
  <TextBefore>From the six safety essentials, a </TextBefore>
  <TextAfter>is essential before any work is carried out?</TextAfter>
  <Items>
    <Item>
      <Text>Answer 1</Text>
    </Item>
    <Item>
      <Text>Answer 2</Text>
    </Item>
    <Item>
      <Text>Answer 3</Text>
    </Item>
    <Item>
      <Text>Answer 4</Text>
    </Item>
    <Item>
      <Text>Answer 5</Text>
    </Item>
    <Item>
      <Text>Answer 6</Text>
    </Item>

Thank you in advance.


Solution

  • OK since SQL 2005 you can use XPath to query the data. I would recommend using the XML column type for the Settings column. Then you can use CROSS APPLY to get the Item nodes.

    SELECT q.TestId,q.SectionId,x.XmlCol.value('(Text)[1]','VARCHAR(MAX)') 
        FROM Questions q
    CROSS APPLY q.settings.nodes('/Settings/Items/Item') x(XmlCol);
    

    If you for some reason cannot change the type of you settings column you could cast it in your statement.

    SELECT q.TestId,q.SectionId,x.XmlCol.value('(Text)[1]','VARCHAR(MAX)') 
        FROM (SELECT TestId,SectionId, cast([settings] as xml) as Data FROM Questions) q
    CROSS APPLY q.settings.nodes('/Settings/Items/Item') x(XmlCol);