将行合并到列

时间:2015-09-03 13:15:49

标签: sql sql-server

我有以下情况(严重抽象,请忽略不良设计):

CREATE TABLE dbo.PersonTest (Id INT, name VARCHAR(255))

INSERT INTO dbo.PersonTest
        (Id, name )
VALUES  (1, 'Pete')
,       (1, 'Marie')
,       (2, 'Sam')
,       (2, 'Daisy')

我正在寻找以下结果:

Id  Name1   Name2
1   Marie   Pete
2   Daisy   Sam

因此,对于每个Id,应该合并行。

获得此结果我使用了以下查询:

WITH PersonRN AS
(
    SELECT  *
    ,       ROW_NUMBER() OVER(PARTITION BY Id ORDER BY name) RN
    FROM    dbo.PersonTest
)

SELECT      PT1.Id
,           PT1.name Name1
,           PT2.name Name2
FROM        PersonRN AS PT1
LEFT JOIN   PersonRN AS PT2 -- Left join in case there's only 1 name
        ON  PT2.Id = PT1.Id
        AND PT2.RN = 2
WHERE       PT1.RN = 1

哪种效果很好。

我的问题是:这是最佳方式(在性能和弹性方面最佳)吗?例如,如果其中一个Id具有第三个名称,则我的查询将忽略此第三个名称。我认为最好的处理方式是动态SQL,这很好,但如果没有动态可以做,我宁愿这样做。

1 个答案:

答案 0 :(得分:4)

除了动态PIVOT之外,您可以使用Dynamic Crosstab来执行此操作,我更喜欢这种可读性。

SQL Fiddle

DECLARE @sql1 VARCHAR(1000) = '',
        @sql2 VARCHAR(1000) = '',
        @sql3 VARCHAR(1000) = ''
DECLARE @max INT

SELECT TOP 1 @max = COUNT(*) FROM PersonTest GROUP BY ID ORDER BY COUNT(*) DESC

SELECT @sql1 = 
'SELECT
    ID' + CHAR(10)

SELECT @sql2 = @sql2 +
'   , MAX(CASE WHEN RN =' + CONVERT(VARCHAR(5), RN) 
    + ' THEN name END) AS ' + QUOTENAME('Name' + CONVERT(VARCHAR(5), RN)) + CHAR(10)
FROM(
    SELECT TOP(@max)
        ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RN
    FROM sys.columns
)t
ORDER BY RN

SELECT @sql3 =
'FROM(
    SELECT *,
        RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY name)
    FROM PersonTest
)t
GROUP BY ID
ORDER BY ID'

PRINT (@sql1 + @sql2 + @sql3)
EXEC (@sql1 + @sql2 + @sql3)