在SQL Server中创建具有多个聚合函数的动态PIVOT表

问题描述 投票:1回答:2

我正在尝试创建一个数据透视表。消息来源如下:

+--------+------------+-----------+--------+--------+------------+
| 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. 对Date,Param1和Param2使用3个聚合函数

注意:Param1Param2列具有预定义的值(每个大约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]

但逻辑仍然让我感到困惑。请帮忙。

sql sql-server pivot pivot-table
2个回答
1
投票

此查询将创建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
投票

如评论中提到的那样,您需要使用动态SQL。根据您的(小)样本,这是有效的,如果您需要对其进行任何更改以满足您的需求,则由您决定。 PRINT声明是你的朋友(如果你有超过4,000个字符,你可能需要更改为SELECT):

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

© www.soinside.com 2019 - 2024. All rights reserved.