T-SQL Pivot - 总行和动态列

时间:2013-08-06 13:06:57

标签: sql tsql sql-server-2008-r2 pivot

让我们直接进入它。这是代码

SELECT [prov], [201304], [201305], [201306], [201307]
FROM (
SELECT [prov], [arrival], [Amount]
FROM [tblSource]) up
PIVOT (SUM([Amount]) FOR [arrival] IN ([201304], [201305], [201306], [201307])) AS pvt
GO

它让我回到了一张如此可爱的桌子。我想知道如何获得每个“日期”列的总数以显示在附加的最后一行?

此外,基础表将添加更多数据,特别是更多日期。这意味着接下来将添加 201308 ,然后 201309

这意味着目前我必须每月修改上面的代码以反映添加内容。有没有办法解决?

2 个答案:

答案 0 :(得分:2)

您可以使用动态SQL动态创建列,但是,我建议在为其设计的层中处理动态枢轴,例如SSRS或Excel。

DECLARE @SQL NVARCHAR(MAX) = '',
        @SQL2 NVARCHAR(MAX) = '',
        @SQL3 NVARCHAR(MAX) = '';

-- COMPILE THE UNIQUE VALUES FOR ARRIVAL THAT NEED TO BE PIVOTED
SELECT  @SQL = @SQL + ',' + QUOTENAME(Arrival),
        @SQL2 = @SQL2 + '+ISNULL(' + QUOTENAME(Arrival) + ', 0)',
        @SQL3 = @SQL3 + ',' + QUOTENAME(Arrival) + ' = ISNULL(' + QUOTENAME(Arrival) + ', 0)'
FROM    (SELECT DISTINCT Arrival FROM tblSource) s;

-- COMBINE THEM INTO A SINGLE QUERY
SET @SQL = 'SELECT [Prov]' + @SQL3 + ', [Total] = ' + STUFF(@SQL2, 1, 1, '') + '
            FROM    (   SELECT  Arrival, Prov, Amount
                        FROM    [tblSource]
                        UNION ALL
                        SELECT  Arrival, ''Total'', SUM(Amount)
                        FROM    [tblSource]
                        GROUP BY Arrival
                    ) up
                    PIVOT
                    (   SUM(Amount)
                        FOR Arrival IN (' + STUFF(@SQL, 1, 1, '') + ')
                    ) pvt;';

-- EXECUTE THE QUERY
EXECUTE SP_EXECUTESQL @SQL;

这将创建并执行以下SQL:

SELECT  [Prov],
        [2013-01-01] = ISNULL([2013-01-01], 0),
        [2013-02-01] = ISNULL([2013-02-01], 0), 
        [Total] = ISNULL([2013-01-01], 0) + ISNULL([2013-02-01], 0)
FROM    (   SELECT  Arrival, Prov, Amount
            FROM    [tblSource]
            UNION ALL
            SELECT  Arrival, 'Total', SUM(Amount)
            FROM    [tblSource]
            GROUP BY Arrival
        ) up
        PIVOT
        (   SUM(Amount)
            FOR Arrival IN ([2013-01-01],[2013-02-01])
        ) pvt;

子查询up中的联合下方的查询在底部添加了总行,并且通过添加行中的所有列来简单地创建行总数。

<强> Example on SQL Fiddle

我会再次强调,我真的建议在SQL之外处理这样的数据操作。

修改

使用UNION获取总行的另一种方法是使用GROUPING SETS,如下所示:

DECLARE @SQL NVARCHAR(MAX) = '',
        @SQL2 NVARCHAR(MAX) = '',
        @SQL3 NVARCHAR(MAX) = '';

-- COMPILE THE UNIQUE VALUES FOR ARRIVAL THAT NEED TO BE PIVOTED
SELECT  @SQL = @SQL + ',' + QUOTENAME(Arrival),
        @SQL2 = @SQL2 + '+ISNULL(' + QUOTENAME(Arrival) + ', 0)',
        @SQL3 = @SQL3 + ',' + QUOTENAME(Arrival) + ' = ISNULL(' + QUOTENAME(Arrival) + ', 0)'
FROM    (SELECT DISTINCT Arrival FROM tblSource) s;

-- COMBINE THEM INTO A SINGLE QUERY
SET @SQL = 'SELECT [Prov]' + @SQL3 + ', [Total] = ' + STUFF(@SQL2, 1, 1, '') + '
            FROM    (   SELECT  Arrival, Prov = ISNULL(Prov, 'Total'), Amount = SUM(Amount)
                        FROM    [tblSource]
                        GROUP BY GROUPING SETS((Prov, arrival), (arrival))
                    ) up
                    PIVOT
                    (   SUM(Amount)
                        FOR Arrival IN (' + STUFF(@SQL, 1, 1, '') + ')
                    ) pvt;';

-- EXECUTE THE QUERY
EXECUTE SP_EXECUTESQL @SQL;

答案 1 :(得分:0)

样本表

CREATE TABLE #TEMP([prov] VARCHAR(100),[arrival] INT, AMOUNT NUMERIC(12,2))

INSERT INTO #TEMP
SELECT 'A' [prov],'201304' [arrival],100 AMOUNT
UNION ALL
SELECT 'A' ,'201305' ,124 
UNION ALL
SELECT 'A' ,'201306' ,156
UNION ALL
SELECT 'B' ,'201304' ,67 
UNION ALL
SELECT 'B' ,'201305' ,211 
UNION ALL
SELECT 'B' ,'201306' ,176 
UNION ALL
SELECT 'C' ,'201304' ,43 
UNION ALL
SELECT 'C' ,'201305' ,56 
UNION ALL
SELECT 'C' ,'201306' ,158

<强> QUERY

您可以使用ROLLUP获取行总数。有关ROLLUP的更多信息here

-- Get the columns for dynamic pivot
DECLARE @cols NVARCHAR (MAX)

SELECT @cols = COALESCE (@cols + ',[' + CAST([arrival] AS VARCHAR(50)) + ']', 
                '[' + CAST([arrival] AS VARCHAR(50)) + ']')
               FROM (SELECT DISTINCT [arrival] FROM  #TEMP) PV 
               ORDER BY [arrival] 

-- Replace NULL value with zero
DECLARE @NulltoZeroCols NVARCHAR (MAX)

SELECT @NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+[arrival]+'],0) AS ['+[arrival]+']' 
FROM (SELECT DISTINCT CAST([arrival] AS VARCHAR(50)) [arrival] FROM #TEMP)TAB  
ORDER BY CAST([arrival]AS INT) FOR XML PATH('')),2,8000) 


DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT [prov],' + @NullToZeroCols + ' FROM 
             (
                 SELECT 
                 ISNULL([prov],''Total'')[prov], 
                 SUM(AMOUNT)AMOUNT , 
                 ISNULL(CAST([arrival] AS VARCHAR(50)),''Total'')[arrival]             
                 FROM #TEMP                 
                 GROUP BY [arrival],[prov]
                 WITH ROLLUP
             ) x
             PIVOT 
             (
                 MIN(AMOUNT)
                 FOR [arrival] IN (' + @cols + ')
            ) p
            ORDER BY CASE WHEN ([prov]=''Total'') THEN 1 ELSE 0 END,[prov]' 

EXEC SP_EXECUTESQL @query

注意:如果您不想将NULL替换为zero,请在动态数据透视的外部查询中将@NullToZeroCols替换为@cols