解析以逗号分隔的列并转置为行

Parsing a comma delimited column and transposing into rows

假设我有一个像这样的 table,其中一列中有不确定数量的逗号分隔值:

thingID    personID

1          123,234,345

2          456,567

我想把它变成这样的形式:

thingID    personID

1          123

1          234

1          345

2          456

2          567

我这样做的最佳选择是什么? 哦,我应该提到数据在 SQL 2008 R2 数据库中,所以我可能无法使用最新的功能。

使用 CROSS APPLY 和字符串拆分功能。
要找到最适合您的字符串拆分函数,请阅读 Aaron Bertrand 的 Split strings the right way – or the next best way.

对于这个演示,我选择使用 SplitStrings_XML 函数,因为它是文章中的第一个纯 t-sql 函数:

CREATE FUNCTION dbo.SplitStrings_XML
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );
GO

现在我们有了一个字符串拆分函数,创建并填充示例 table( 在您以后的问题中省去这一步):

DECLARE @T AS TABLE
(
    thingID int,
    personID varchar(max)
)

INSERT INTO @T VALUES
(1, '123,234,345'),
(2, '456,567')

查询:

SELECT thingId, Item
FROM @T 
CROSS APPLY dbo.SplitStrings_XML(personID, ',')

结果:

thingId     Item
1           123
1           234
1           345
2           456
2           567

You can see a live demo on rextester.

有几种方法可以做到这一点。以下是 SQL Server 2008 的两种方法:

XML-方法: 要求字符串允许 xml-技巧(没有无效的 XML 字符)

SELECT a.thingID, Split.a.value('.', 'VARCHAR(100)') AS Data  
FROM (SELECT OtherID,  
             CAST('<M>' + REPLACE(personID, ',', '</M><M>') + '</M>' AS XML) AS Data  
      FROM table1) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);

递归方法:

;WITH tmp(thingID, DataItem, Data) AS (
    SELECT thingID, LEFT(personID, CHARINDEX(',', personID + ',') - 1),
        STUFF(personID, 1, CHARINDEX(',', personID + ','), '')
    FROM table1
    UNION ALL
    SELECT thingID, LEFT(personID, CHARINDEX(',', personID + ',') - 1),
        STUFF(personID, 1, CHARINDEX(',', personID + ','), '')
    FROM tmp
    WHERE Data > ''
)
SELECT thingID, DataItem AS personID
FROM tmp