2016-01-05 2 views
0

У меня динамический Pivot запрос, который выглядит следующим образом:SQL Server Добавить общую строку в Dynamic 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

Союз новое имя столбца с суммы каждого столбца. вам нужно использовать временную таблицу для этого – bmsqldev

+3

Получить динамический запрос для записи в временную таблицу. Выберите из таблицы temp, объедините другой запрос, который выполняет суммирование. Конечно, это было бы намного проще (чище и эффективнее) в коде приложения. – user5151179

+0

Это должно быть сделано инструментом отчетности, а не SQL. Все инструменты отчетности позволяют добавлять заголовки/нижние колонтитулы групп –

ответ

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 для Total, и у вас есть общая строка (ы).

Я бы также advise against variable concatenation (SELECT @Columnname = @ColumnName + SomeField FROM SomeTable) так как результаты не гарантируются. Вместо этого используйте XML extensions to concatenate your rows to columns.

Кроме того, я хотел бы использовать параметризованный запрос, так что вместо:

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, чтобы добавить их в ваш запрос, только для вашего запроса, чтобы преобразовать их обратно в даты, когда он выполняется.

Наконец, я не исправили, но я бы рекомендовал agaainst с помощью 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; 

N.B.Я не тестировал это полностью, так как для этого потребовалось бы создать 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;