SQL Server在Dynamic Pivot中添加总行

时间:2016-01-05 07:52:13

标签: sql-server pivot dynamic-pivot

我有一个动态Pivot查询,如下所示:

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)
DECLARE @start AS DateTime
DECLARE @end AS DateTime
DECLARE @business AS VARCHAR(50)

SET @start = '2015-01-01';
SET @end   = '2015-12-01';
SET @business = 'EUR';

--Get distinct values of the PIVOT Column 
SELECT @ColumnName= ISNULL(@ColumnName + ',','') 
       + QUOTENAME(date1)
FROM (  

    SELECT m.date1, m.date2 FROM(
SELECT DISTINCT CONVERT(nvarchar(50),   DATENAME(m, date) 
                               + ', ' 
                               + DATENAME(yyyy,date)) as date1, date as date2  

        FROM  bus_best where date between @start and  @end                              
        )m 
    )tab order by tab.date2

 SET @DynamicPivotQuery = 
'select * from (
    select sum(bb.value) as value, bb.date as date, c.name as Name from bus_best bb
        join pro p on p.id = bb.id
        join con c on c.id = p.id
        join bus_t bu on bu.id = c.id
        where bb.date between '''+  cast     (@start as VARCHAR(50))+''' and '''+  cast     (@end as VARCHAR(50))+''' 
        and bu.name = '''+  cast     (@business as VARCHAR(50))+'''
        group by bb.date, c.name        
            ) as t

            PIVOT(SUM(t.value) 
          FOR date IN (' + @ColumnName + ')) AS PVTTable'

            EXEC sp_executesql @DynamicPivotQuery

,输出类似于:

Name   Jan    Feb    March   April  May   June  July ....
----------------------------------------------------------    
Name1  32     654    1        42    342   4     4543
Name2  54      3    234       43    453   432    22 
Name3  55      12   56       1234   43    643    12
Name4  77     235   3566    35635   23    2    3462

我想要的只是在最后一行添加所有行的总和,如:

Name   Jan    Feb    March   April  May   June  July ....
----------------------------------------------------------    
Name1  32     654    1        42    342   4     4543
Name2  54      3    234       43    453   432    22 
Name3  55      12   56       1234   43    643    12
Name4  77     235   3566    35635   23    2    3462
Total ...     ....  ....    ....   ....   ....  ..... 

1 个答案:

答案 0 :(得分:1)

使用GROUPING SETS,您可以将总行数添加到子查询中,如果您有查询,则可以作为一个简单示例:

SELECT  A, B, SUM(C) AS C
FROM    T
GROUP BY A, B;

这会给你:

A       B       C
-------------------
1       1       5
1       2       3
2       1       8
2       2       1

如果您使用分组集如下

SELECT  A, B, SUM(C) AS C
FROM    T
GROUP BY GROUPING SETS ((A, B), (A));

你得到了

A       B       C
-------------------
1       1       5
1       2       3
1       NULL    8   -- Total for A = 1
2       1       8
2       2       1
2       NULL    9   -- Total for A = 2

这相当于:

SELECT  A, B, SUM(C) AS C
FROM    T
GROUP BY A, B
UNION ALL
SELECT  A, NULL, SUM(C) AS C
FROM    T
GROUP BY A;

因此,每个分组集基本上代表了另一个查询,但内部SQL Server能够重用聚合,因此更有效。您需要做的只是替换NULL的{​​{1}}值,并且您有总行数。

我也会advise against variable concatenationTotal)因为结果不能保证是正确的。而是使用XML extensions to concatenate your rows to columns

另外,我会使用参数化查询,而不是:

SELECT @Columnname = @ColumnName + SomeField FROM SomeTable

改为使用:

DECLARE @Variable VARCHAR(10) = 'TEST';
SET @DynamicPivotQuery = 'SELECT * FROM T WHERE Column = ''' + @Variable + '''';
EXECUTE sp_executesql @DynamicPivotQuery;

这为您提供了正确的类型化参数,因此无需将日期转换为varchars以将其添加到查询中,只是为了使查询在执行时必须将它们转换回日期。

最后,我还没有纠正这个问题,但是在处理日期时我会建议使用DECLARE @Variable VARCHAR(10) = 'TEST'; SET @DynamicPivotQuery = 'SELECT * FROM T WHERE Column = @Param'; EXECUTE sp_executesql @DynamicPivotQuery, N'@Param VARCHAR(10)', @Param = @Variable; ,在下面的文章中可以很好地总结出这个原因:

最后查询:'

BETWEEN

N.B。我还没有完全测试过,因为它需要创建4个表,我只能猜测数据,但是如果有一些小的语法错误,答案和链接中有足够的信息可以让你走上正确的轨道

完整的工作示例

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)
DECLARE @start AS DATETIME
DECLARE @end AS DATETIME
DECLARE @business AS VARCHAR(50)

SET @start = '2015-01-01';
SET @end   = '2015-12-01';
SET @business = 'EUR';

--Get distinct values of the PIVOT Column 
-- Uses "DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)" to get the first of each
-- month then converts this to the format "yyyymmdd" (this is culture insensitive)
SET @ColumnName = 
        STUFF(( SELECT  ',' + QUOTENAME(CONVERT(VARCHAR(10), D.[Date], 112))
                FROM (  SELECT  [Date] = DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)
                        FROM    bus_best
                        WHERE   [Date] BETWEEN @start AND @end
                        GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)
                    ) AS d
                ORDER BY d.[Date]
                FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'), 1, 1, '');


SET @DynamicPivotQuery = 
    'SELECT Name, ' +  @ColumnName + '
    FROM    (   SELECT  SUM(bb.value) AS Value, 
                        Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, bb.date), 0), 
                        ISNULL(c.name, ''Total'') AS Name 
                FROM    bus_best bb
                        INNER JOIN pro AS p ON p.id = bb.id
                        INNER JOIN con AS c ON c.id = p.id
                        INNER JOIN bus_t AS bu ON bu.id = c.id
                WHERE   bb.date BETWEEN @StartParam AND @EndParam
                AND     bu.name = @BusinessParam
                GROUP BY GROUPING SETS 
                        (   (DATEADD(MONTH, DATEDIFF(MONTH, 0, bb.date), 0), c.name), 
                            (DATEADD(MONTH, DATEDIFF(MONTH, 0, bb.date), 0))
                        )
            ) AS t
            PIVOT
            (   SUM(t.value) 
                FOR date IN (' + @ColumnName + ')
            ) AS PVTTable;';

EXECUTE sp_executesql 
    @DynamicPivotQuery, 
    N'@StartParam DATETIME, @EndParam DATETIME, @BusinessParam VARCHAR(50)',
    @StartParam = @Start,
    @EndParam = @End,
    @BusinessParam = @Business;