Создание динамической таблицы PIVOT с несколькими агрегированными функциями в SQL Server - PullRequest
1 голос
/ 19 апреля 2019

Я пытаюсь создать сводную таблицу.Исходный код следующий:

+--------+------------+-----------+--------+--------+------------+
| UserId | LastName   | FirstName | Param1 | Param2 | Date       |
+--------+------------+-----------+--------+--------+------------+
| 1      | Snow       | John      | Text1  | Text1  | 01-01-2019 |
| 2      | Lannister  | Tyrion    | Text1  | Text1  | 01-01-2019 |
| 3      | Targaryen  | Daenerys  | Text2  | Text2  | 01-01-2019 |
| 1      | Snow       | John      | Text3  | Text2  | 01-02-2019 |
| 2      | Lannister  | Tyrion    | Text3  | Text2  | 01-02-2019 |
| 3      | Targaryen  | Daenerys  | Text3  | Text3  | 01-02-2019 |
|        |            |           |        |        | … 120 days |
+--------+------------+-----------+--------+--------+------------+

Вот чего я хочу добиться:

+--------+------------+-----------+-------------------+-------------------+-------------------+-------------------+---+
| UserId | LastName   | FirstName | Param1:01-01-2019 | Param2:01-01-2019 | Param1:01-02-2019 | Param2:01-02-2019 | … |
+--------+------------+-----------+-------------------+-------------------+-------------------+-------------------+---+
| 1      | Snow       | John      | Text1             | Text1             | Text3             | Text2             | … |
| 2      | Lannister  | Tyrion    | Text1             | Text1             | Text3             | Text2             | … |
| 3      | Targaryen  | Daenerys  | Text2             | Text2             | Text3             | Text3             | … |
+--------+------------+-----------+-------------------+-------------------+-------------------+-------------------+---+

Итак, в основном я пытаюсь решить 2 вопроса:

  1. Динамически создавать 120 столбцов для дат.
  2. Использовать 3 агрегатных функции для даты, параметра 1 и параметра 2

Примечание: Param1 и Param2столбцы имеют предопределенные значения (около 10 каждый)

Мой начальный статический запрос сводки выглядит так:

WITH PivotData AS
(
      SELECT 
           [UserId]
          ,[Last Name]
          ,[First Name]
          ,[Param1]
          ,[Param2]
          ,[Date]
      FROM [dbo].[MyTable]
)
SELECT [Last Name], [First Name], [Param1:01-01-2019], [Param2:01-01-2019], [Param1:01-02-2019], [Param2:01-02-2019]
FROM PivotData
       PIVOT ( MAX([Param1]) FOR [Date] in ([Param1:01-01-2019], [Param1:01-01-2019]) ) AS P1
       PIVOT ( MAX([Param2) FOR [Date] in ([Param2:01-02-2019], [Param2:01-02-2019]) ) AS P2

Обновление 1:

У меня естьиспользовал другой запрос, но все равно нужно делать это динамически, поэтому оригинальный вопрос остается

SELECT
[UserId]

MAX(CASE WHEN [Date] = '2019-01-01' THEN ISNULL([Param1], NULL) ELSE NULL END) AS [Param1:2019-01-01],
MAX(CASE WHEN [Date] = '2019-01-01' THEN ISNULL([Param2], NULL) ELSE NULL END) AS [Param2:2019-01-01],
MAX(CASE WHEN [Date] = '2019-01-01' THEN ISNULL([Param3], NULL) ELSE NULL END) AS [Param3:2019-01-01],

MAX(CASE WHEN [Date] = '2019-01-02' THEN ISNULL([Param1], NULL) ELSE NULL END) AS [Param1:2019-01-02],
MAX(CASE WHEN [Date] = '2019-01-02' THEN ISNULL([Param2], NULL) ELSE NULL END) AS [Param2:2019-01-02],
MAX(CASE WHEN [Date] = '2019-01-02' THEN ISNULL([Param3], NULL) ELSE NULL END) AS [Param3:2019-01-02],

FROM [dbo].[MyTable]
GROUP BY [UserId]
ORDER BY [UserId]

Но логика все еще смущает меня.Пожалуйста помоги.

Ответы [ 2 ]

1 голос
/ 19 апреля 2019

Этот запрос создаст список из 120 дат, чтобы затем сгенерировать группу запрошенных столбцов.

DECLARE @SQL NVARCHAR(MAX);

WITH 
E(n) AS( --11 rows
    SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)
),
E2(n) AS( --11x11= 121 rows
    SELECT a.n FROM E a, E b
),
cteTally(calDate) AS(
    SELECT TOP( 120)
        DATEADD( dd, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) -1, '20190101') calDate
    FROM E2
)
SELECT @SQL = 
    --Identify first static part of query. Columns that won't be pivoted.
    N'SELECT [UserId]'
    --Identify the dynamic part and set the pattern. Use FOR XML PATH to concatenate the values.
    + ( SELECT N'
            ,MAX(CASE WHEN [Date] = ''' + CONVERT( NCHAR(8), calDate, 112) + ''' THEN ISNULL([Param1], NULL) ELSE NULL END) AS [Param1:' + CONVERT( NCHAR(10), calDate, 120) + ']
            ,MAX(CASE WHEN [Date] = ''' + CONVERT( NCHAR(8), calDate, 112) + ''' THEN ISNULL([Param2], NULL) ELSE NULL END) AS [Param2:' + CONVERT( NCHAR(10), calDate, 120) + ']
            ,MAX(CASE WHEN [Date] = ''' + CONVERT( NCHAR(8), calDate, 112) + ''' THEN ISNULL([Param3], NULL) ELSE NULL END) AS [Param3:' + CONVERT( NCHAR(10), calDate, 120) + ']'
        FROM cteTally
        FOR XML PATH(''), TYPE).value('./text()[1]', 'nvarchar(max)')
    --Identify second static part of query. Everything starting with the FROM clause.
    + N'FROM [dbo].[MyTable]
GROUP BY [UserId]
ORDER BY [UserId];
DECLARE @SQL NVARCHAR(MAX);'

--Used for debugging purposes
PRINT @SQL

--Execute the dynamic query. Use parameters when needed.
EXEC sp_executesql @SQL --, @ParamDefinition, @Param1, @Param2, ..., @ParamN;
GO
1 голос
/ 19 апреля 2019

Как уже упоминалось в комментариях, вам нужно использовать динамический SQL.На основании вашей (небольшой) выборки это работает, однако, вам решать, нужно ли вам вносить в него какие-либо изменения в соответствии с вашими потребностями.оператор PRINT - ваш друг (который, возможно, вам придется изменить на SELECT, если у вас более 4000 символов):

CREATE TABLE dbo.SampleTable (UserId int,
                              LastName varchar(50),
                              FirstName varchar(50),
                              Param1 varchar(6),
                              Param2 varchar(6),
                              [Date] date);
GO

INSERT INTO dbo.SampleTable
VALUES (1,'Snow','John','Text1','Text1','20190101'),
       (2,'Lannister','Tyrion','Text1','Text1','20190101'),
       (3,'Targaryen','Daenerys','Text2','Text2','20190101'),
       (1,'Snow','John','Text3','Text2','20190102'),
       (2,'Lannister','Tyrion','Text3','Text2','20190102'),
       (3,'Targaryen','Daenerys','Text3','Text3','20190102');
GO

DECLARE @SQL nvarchar(MAX);

SET @SQL = N'SELECT UserId,' + NCHAR(13) + NCHAR(10) +
           STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
                         N'       MAX(CASE [Date] WHEN ' + QUOTENAME(CONVERT(varchar(8),ST.[Date],112),'''') + N' THEN ' + QUOTENAME(C.COLUMN_NAME) + N' END) AS ' + QUOTENAME(C.COLUMN_NAME + N':' + REPLACE(CONVERT(varchar(10),ST.[Date],102),N'.',N'-'))
                  FROM INFORMATION_SCHEMA.COLUMNS C
                       CROSS JOIN (SELECT DISTINCT [Date]
                                   FROM dbo.SampleTable) ST
                  WHERE C.TABLE_SCHEMA = N'dbo'
                    AND C.TABLE_NAME = N'SampleTable'
                    AND C.COLUMN_NAME LIKE N'Param%'
                  ORDER BY ST.[Date],
                           C.ORDINAL_POSITION                           
                  FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + NCHAR(13) + NCHAR(10) + 
          N'FROM dbo.SampleTable' + NCHAR(13) + NCHAR(10) + 
          N'GROUP BY UserId' + NCHAR(13) + NCHAR(10) + 
          N'ORDER BY UserId;';
PRINT @SQL; --Your Debugging best friend
EXEC sp_executesql @SQL;

GO
DROP TABLE SampleTable

db <> fiddle

...