将多行数据合并为一行 - SQL Server

时间:2015-01-08 09:53:14

标签: sql sql-server

我有一张看起来有点像这样的桌子

ID          NAME          MODULE          STARTDATE          ENDDATE          MARK
123456      J Bloggs      Module 1        13/01/2014         30/04/2014       FAIL
123456      J Bloggs      Module 1        13/05/2014         30/08/2014       FAIL
123456      J Bloggs      Module 1        13/09/2014         30/12/2014       PASS
123456      J Bloggs      Module 2        13/05/2014         30/08/2014       PASS
123456      J Bloggs      Module 3        13/01/2015         30/04/2015       FAIL
234567      A Test        Module 1        13/01/2014         30/04/2014       PASS
234567      A Test        Module 2        13/05/2014         30/08/2014       FAIL
234567      A Test        Module 2        13/09/2014         30/12/2014       PASS

该表包含大量数据,结构与此类似。我想要做的是几乎将来自多行的一些数据连接成基于学生和模块的单行结构,因此最终结果看起来像

ID          NAME          MODULE          ENDDATE1          ENDDATE2          ENDDATE3          MARK
123456      J Bloggs      Module 1        30/04/2014        30/08/2014        30/12/2014        PASS
123456      J Bloggs      Module 2        30/08/2014                                            PASS
123456      J Bloggs      Module 3        30/04/2015                                            FAIL
234567      A Test        Module 1        30/04/2014                                            PASS
234567      A Test        Module 2        30/08/2014        30/12/2014                          PASS

因此,新表将根据模块显示同一行上的所有结束日期,然后将显示最新标记“max(Mark)”。示例表中显示的可能结束日期可能超过3个,因为这完全取决于原始表格以及学生可能需要“重新启动”模块的次数(最多可达4/5)在某些情况下的时间)。

1 个答案:

答案 0 :(得分:0)

尝试使用Dynamic Pivot:

IF(OBJECT_ID('tempdb..#table') IS NOT NULL)
DROP TABLE #TABLE
CREATE TABLE  #TABLE (ID INT, NAME VARCHAR(30),MODULE VARCHAR(30),STARTDATE VARCHAR(30),ENDDATE VARCHAR(30),MARK VARCHAR(30))

INSERT INTO #TABLE VALUES
(123456, 'J Bloggs', 'Module 1', '13/01/2014', '30/04/2014', 'FAIL'),
(123456, 'J Bloggs', 'Module 1', '13/05/2014', '30/08/2014', 'FAIL'),
(123456, 'J Bloggs', 'Module 1', '13/09/2014', '30/12/2014', 'PASS'),
(123456, 'J Bloggs', 'Module 2', '13/05/2014', '30/08/2014', 'PASS'),
(123456, 'J Bloggs', 'Module 3', '13/01/2015', '30/04/2015', 'FAIL'),
(234567, 'A Test', 'Module 1', '13/01/2014', '30/04/2014', 'PASS'),
(234567, 'A Test', 'Module 2', '13/05/2014', '30/08/2014', 'FAIL'),
(234567, 'A Test', 'Module 2', '13/09/2014', '30/12/2014', 'PASS')
DECLARE @Columns VARCHAR(MAX)

SELECT @Columns = STUFF((SELECT ',' + '[' + CONVERT(VARCHAR(30), number, 121) + ']'
                         FROM   master..spt_values N
                         WHERE  n.number BETWEEN 1 AND (SELECT TOP 1 COUNT(Enddate)
                                                        FROM   #TABLE
                                                        GROUP  BY ID,NAME,MODULE
                                                        ORDER  BY COUNT(Enddate) DESC)
                                AND TYPE = 'P'
                         FOR XML PATH('')), 1, 1, '')
DECLARE @sql NVARCHAR(MAX) = '
SELECT ID,
       NAME,
       MODULE,
       '+@Columns+',
       (SELECT TOP 1 MARK
        FROM   #table t1
        WHERE  pvt.ID = t1.ID
               AND pvt.NAME = t1.NAME
               AND pvt.MODULE = t1.MODULE
        ORDER  BY enddate DESC) AS MARK
FROM   (SELECT ID,
               NAME,
               MODULE,
               ENDDATE,
               ROW_NUMBER()
                 OVER(
                   partition BY ID, NAME, MODULE
                   ORDER BY enddate) AS rn
        FROM   #table) t
       PIVOT ( Max(ENDDATE)
             FOR rn IN('+@Columns+')) AS pvt 
'

EXEC sp_executeSQL @sql

检查Pivot和Unpivot here的详细信息。

注意:正如您所看到的,我使用子查询来查找最新的Mark。我尽力用更好的东西代替它,但我做不到。不过,它对你有用。