通过select语句将xml节点作为逗号分隔列表

时间:2016-11-28 16:56:45

标签: sql-server tsql

Xml存储在varchar列中。

其中一个节点就像

<PreviousItem>
<string>501</string>
<string>502</string>
<string>505</string>
</PreviousItem>

我需要将此节点元素作为comma delimited list (having issue with this part only)以及此xml和表中的其他项。

SELECT
     nId, cDescription, 
    ,CAST(cSettings AS XML).value('data(/clsSettings/PreviousItem)[1]','nvarchar(max)') AS PreviousItem
    ,CAST(cSettings AS XML).value('data(/clsSettings/RegistrationType)[1]','nvarchar(50)') AS RegistrationType
FROM tblX 

以上是我将PreviousItem作为501502505

我已尝试过关注,但我在Incorrect syntax near the keyword 'AS'

中获得了CAST(cSettings AS XML)
( SELECT    
            STUFF(( SELECT  ',' + Prods.Prod.value('text()[1]','varchar(max)')
                    FROM    CAST(cSettings AS XML).nodes('/clsSettings/PreviousItem') AS Prods ( Prod )
                    FOR XML PATH('')
            ), 1, 1, '')
        ) prods

2 个答案:

答案 0 :(得分:1)

这是一种方法:

-- Sample Data
DECLARE @tblX TABLE (cID int identity, cSettings xml);
INSERT @tblX (cSettings)
VALUES 
('<PreviousItem>
<string>501</string>
<string>502</string>
<string>505</string>
</PreviousItem>'),
('<PreviousItem>
<string>4433</string>
<string>5577</string>
</PreviousItem>');

-- Solution
SELECT 
  cID,
  PreviousItem = STUFF
  (
    (
      SELECT ',' + x.value('(text())[1]', 'varchar(500)')
      FROM @tblX t
      CROSS APPLY cSettings.nodes('/PreviousItem/string') x(x)
      WHERE t.cID = tx.cId
      FOR XML PATH('')
    ),1,1,''
  )
FROM @tblX tx;

根据您的示例数据,您有一个存在于XML中的RegistrationType,您需要获取...这是我的第一个解决方案的调整版本。

-- Updated Sample Data with RegistrationType
DECLARE @tblX TABLE (cID int identity, cSettings xml);
INSERT @tblX (cSettings)
VALUES 
('<PreviousItem>
<string>501</string>
<string>502</string>
<string>505</string>
</PreviousItem>
<RegistrationType>Type A</RegistrationType>
'),
('<PreviousItem>
<string>4433</string>
<string>5577</string>
</PreviousItem>
<RegistrationType>Type B</RegistrationType>
');

-- Solution which includes RegistrationType
SELECT 
  cID,
  PreviousItem = STUFF
  (
    (
      SELECT ',' + x.value('(text())[1]', 'varchar(500)')
      FROM @tblX t
      CROSS APPLY cSettings.nodes('/PreviousItem/string') x(x)
      WHERE t.cID = tx.cId
      FOR XML PATH('')
    ),1,1,''
  ),    
  RegistrationType = STUFF
  (
    (
      SELECT ',' + x.value('(text())[1]', 'varchar(500)')
      FROM @tblX t
      CROSS APPLY cSettings.nodes('RegistrationType') x(x)
      WHERE t.cID = tx.cId
      FOR XML PATH('')
    ),1,1,''
  )
FROM @tblX tx;

要获得更准确的解决方案,请提供一些DDL和耗材样本数据,我可以相应地修改我的解决方案。

更新:因为您的XML数据存储为文本,您需要调整原始解决方案,如下所示:

-- Updated Sample Data with RegistrationType
DECLARE @tblX TABLE (cID int identity, cSettings varchar(max));
INSERT @tblX (cSettings)
VALUES 
('<PreviousItem>
<string>501</string>
<string>502</string>
<string>505</string>
</PreviousItem>
<RegistrationType>Type A</RegistrationType>
'),
('<PreviousItem>
<string>4433</string>
<string>5577</string>
</PreviousItem>
<RegistrationType>Type B</RegistrationType>
');

-- Solution which includes RegistrationType
SELECT 
  cID,
  PreviousItem = STUFF
  (
    (
      SELECT ',' + x.value('(text())[1]', 'varchar(500)')
      FROM @tblX t
      CROSS APPLY (VALUES (CAST(cSettings AS xml))) xx(xx)
      CROSS APPLY xx.xx.nodes('/PreviousItem/string') x(x)
      WHERE t.cID = tx.cId
      FOR XML PATH('')
    ),1,1,''
  ),
  RegistrationType = STUFF
  (
    (
      SELECT ',' + x.value('(text())[1]', 'varchar(500)')
      FROM @tblX t
      CROSS APPLY (VALUES (CAST(cSettings AS xml))) xx(xx)
      CROSS APPLY xx.xx.nodes('RegistrationType') x(x)
      WHERE t.cID = tx.cId
      FOR XML PATH('')
    ),1,1,''
  )
FROM @tblX tx;

答案 1 :(得分:0)

因为你正在处理文本而不是XML,你可以使用一些基本的T-SQL和PatExclude8K来更有效地处理这个问题,如下所示:

-- Updated Sample Data with RegistrationType
DECLARE @tblX TABLE (cID int identity, cSettings varchar(max));
INSERT @tblX (cSettings)
VALUES 
('<PreviousItem>
<string>501</string>
<string>502</string>
<string>505</string>
</PreviousItem>
<RegistrationType>Type A</RegistrationType>
'),
('<PreviousItem>
<string>4433</string>
<string>5577</string>
</PreviousItem>
<RegistrationType>Type B</RegistrationType>
');

-- Solution using PatExclude8K
SELECT
  cID, 
  PreviousItem     = SUBSTRING(NewString,1, LEN(NewString)-1),
  RegistrationType = SUBSTRING
  (
    cSettings,
    CHARINDEX('<RegistrationType>', cSettings)+18,
    CHARINDEX('</RegistrationType>', cSettings) - 
      (CHARINDEX('<RegistrationType>', cSettings)+18)
  )
FROM @tblX o
CROSS APPLY dbo.PatExclude8K(REPLACE(cSettings,'</string>',','), '[^0-9,]');