【问题标题】:T-SQL Pivot - Total Row and Dynamic ColumnsT-SQL Pivot - 总行和动态列
【发布时间】:2013-08-07 12:19:06
【问题描述】:

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

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

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

【问题讨论】:

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


    【解决方案1】:

    您可以使用动态 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中union下面的查询把总行加到了底部,行总就是简单地把该行的所有列加起来。

    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;
    

    【讨论】:

    • 我会将 ROLLUP 添加到您的解决方案中。 OP 请求了一行,其中包含每列的总数。
    • 谢谢 - 我正在将其推送到网页,SSRS 并不适合我们在响应式和移动友好的解决方案中所需的内容,但我确实接受了它。我可能只是将功能构建到页面本身中。谢谢。
    • @bluefeet ROLLUP 是否与 PIVOT 一起使用?我通过向查询添加联合并重复数据来处理 Total 行。我现在添加了一个使用 GROUPING SETS 的替代方法。
    • @GarethD 是的,您可以在枢轴后使用 ROLLUP see this answer
    【解决方案2】:

    示例表

    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
    

    查询

    您可以使用 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

    【讨论】:

      猜你喜欢
      • 2013-08-13
      • 1970-01-01
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      • 2020-10-19
      • 1970-01-01
      • 1970-01-01
      • 2016-10-04
      相关资源
      最近更新 更多