0

我有一个动态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 ...     ....  ....    ....   ....   ....  ..... 
4

1 回答 1

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;

因此,每个 Grouping 集本质上代表一个进一步的查询,但内部 SQL Server 能够重用聚合,因此效率更高。然后,您需要做的就是替换 的NULLTotal,然后您就有了总行数。

我还建议不要使用变量连接( SELECT @Columnname = @ColumnName + SomeField FROM SomeTable),因为不能保证结果是正确的。而是使用XML 扩展将您的行连接到列

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

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

而是使用:

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

这为您提供了正确键入的参数,因此无需将您的日期转换为 varchars 以将它们添加到您的查询中,只需让您的查询在执行时将它们转换回日期。

最后,我还没有更正这一点,但我建议BETWEEN在处理日期时不要使用,以下文章很好地总结了其原因:

这给了你一个最终的查询:'

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;

注意我没有完全测试这个,因为它需要创建4个表,我只能猜测数据,但如果有一些小的语法,希望答案和链接中有足够的信息让你走上正确的轨道错误

完整的工作示例

IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;

CREATE TABLE #T
(
    [Date] DATE,
    Business VARCHAR(50),
    Value INT,
    Name VARCHAR(50)
);
INSERT #T (Date, Business, Value, Name)
VALUES
    ('20150601', 'EUR', 1, 'Group 1'), 
    ('20150605', 'EUR', 12, 'Group 2'), 
    ('20150605', 'EUR', 3, 'Group 3'), 
    ('20150701', 'EUR', 2, 'Group 1'), 
    ('20150708', 'EUR', 2, 'Group 2'), 
    ('20150702', 'EUR', 7, 'Group 3'), 
    ('20150703', 'AAA', 2, 'Group 1');


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 
SET @ColumnName = 
        STUFF(( SELECT  ',' + QUOTENAME(CONVERT(VARCHAR(10), D.[Date], 120))
                FROM (  SELECT  [Date] = DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)
                        FROM    #T
                        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(bb.name, ''Total'') AS Name 
                FROM    #T AS bb 
                WHERE   bb.date BETWEEN @StartParam AND @EndParam
                AND     bb.Business = @BusinessParam
                GROUP BY GROUPING SETS ((bb.date, bb.name), (bb.Date))
            ) 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;
于 2016-01-05T09:40:33.577 回答