如何在 SQL 服务器上使用变量作为数据透视列
How to use variables as pivot columns on SQL Server
我在 MS SQL 服务器上使带有数据透视列的 table 变得更加动态时遇到问题。
我有一个查询从当年的五周期间和上一年的五周期间检索数据,所以列 CUR_YR 和 PREV_YR 是那些来自枢轴子句。这是我的查询:
DECLARE @TODAY DATE
SET @TODAY= CAST(GETDATE() AS DATE)
DECLARE @PREV_YR INT, @CUR_YR INT
SET @CUR_YR = YEAR(@TODAY)
SET @PREV_YR = @CUR_YR-1
SELECT * FROM (
SELECT BUS, DET, [STR], COALESCE(@PREV_YR,0) AS PREV_YR, COALESCE(@CUR_YR,0) AS CUR_YR FROM (
SELECT YR,BUS, DET,[STR],count(ORD_ID_SE) as ORDS FROM (<SUBQUERY>) )AS T
PIVOT(
SUM(ORDS)
FOR YR IN (
@PREV_YR, @CUR_YR)
) AS pivot_table
)AS F
当我手动将 FOR YR IN() 子句中的数据透视列设置为 2020 和 2021 时,查询有效,但由于我需要这些列是动态的(也就是让它们随着时间的推移而改变,所以明年是 2021 年和 2022 年)我使用变量 @PREV_YR 和 @CUR_YR 如代码所示。但是,这会导致错误“'@PREV_YR' 附近的语法不正确。”。
有解决办法吗?也许用不同的方式在 IN() 子句中输入两年。
感谢您的帮助。
这是 PIVOT 功能的已知限制。您的 IN
列表中必须有一组预定义的列。它们不能动态改变。因此,为了做到这一点,您需要利用动态 SQL 根据您的输入值更改结果集。
有很多关于如何执行此操作的指南,但我将举例说明如何将其应用于您的脚本(添加了一些小的重新格式化)。为了 运行 它,我确实纠正了两个问题。 1.Add 子查询的别名 subquery_alias
2. 添加 GROUP BY
子句到聚合查询
DECLARE @TODAY DATE = CAST(GETDATE() AS DATE);
DECLARE @CUR_YR INT = YEAR(@TODAY);
DECLARE @PREV_YR INT = @CUR_YR-1;
DECLARE @Sql nvarchar(max) =
N'SELECT
*
FROM
(SELECT
BUS
,DET
,[STR]
,COALESCE(' + QUOTENAME(CAST(@PREV_YR AS nvarchar(10))) + N',0) AS PREV_YR
,COALESCE(' + QUOTENAME(CAST(@CUR_YR AS nvarchar(10))) + N',0) AS CUR_YR
FROM
(SELECT
YR
,BUS
,DET
,[STR]
,COUNT(ORD_ID_SE) AS ORDS
FROM
(<SUBQUERY>) AS subquery_alias
GROUP BY
YR
,BUS
,DET
,[STR]) AS T
PIVOT(SUM(ORDS) FOR YR IN (' + QUOTENAME(CAST(@PREV_YR AS nvarchar(10))) + N',' + QUOTENAME(CAST(@CUR_YR AS nvarchar(10))) + N')) AS pivot_table) AS F';
EXEC sys.sp_executesql @Sql;
也就是说,在这种情况下,您将数据透视表的输出重新别名分别为 PREV_YR
和 CUR_YR
。所以你真的不需要动态列名,如果你只打算以这种方式使用两个不同的变量,那么使用 SUM(CASE WHEN....END)
方法来转换它更有意义。像这样:
DECLARE @TODAY DATE = CAST(GETDATE() AS DATE);
DECLARE @CUR_YR INT = YEAR(@TODAY);
DECLARE @PREV_YR INT = @CUR_YR-1;
SELECT
BUS
,DET
,[STR]
,SUM(CASE WHEN YR = @PREV_YR THEN 1 ELSE 0 END) AS PREV_YR
,SUM(CASE WHEN YR = @CUR_YR THEN 1 ELSE 0 END) AS CUR_YR
FROM
(<SUBQUERY>) AS subquery_alias
GROUP BY
BUS
,DET
,[STR];
这样做可以完全避免动态 SQL 和更复杂的数据透视查询。
我在 MS SQL 服务器上使带有数据透视列的 table 变得更加动态时遇到问题。
我有一个查询从当年的五周期间和上一年的五周期间检索数据,所以列 CUR_YR 和 PREV_YR 是那些来自枢轴子句。这是我的查询:
DECLARE @TODAY DATE
SET @TODAY= CAST(GETDATE() AS DATE)
DECLARE @PREV_YR INT, @CUR_YR INT
SET @CUR_YR = YEAR(@TODAY)
SET @PREV_YR = @CUR_YR-1
SELECT * FROM (
SELECT BUS, DET, [STR], COALESCE(@PREV_YR,0) AS PREV_YR, COALESCE(@CUR_YR,0) AS CUR_YR FROM (
SELECT YR,BUS, DET,[STR],count(ORD_ID_SE) as ORDS FROM (<SUBQUERY>) )AS T
PIVOT(
SUM(ORDS)
FOR YR IN (
@PREV_YR, @CUR_YR)
) AS pivot_table
)AS F
当我手动将 FOR YR IN() 子句中的数据透视列设置为 2020 和 2021 时,查询有效,但由于我需要这些列是动态的(也就是让它们随着时间的推移而改变,所以明年是 2021 年和 2022 年)我使用变量 @PREV_YR 和 @CUR_YR 如代码所示。但是,这会导致错误“'@PREV_YR' 附近的语法不正确。”。
有解决办法吗?也许用不同的方式在 IN() 子句中输入两年。
感谢您的帮助。
这是 PIVOT 功能的已知限制。您的 IN
列表中必须有一组预定义的列。它们不能动态改变。因此,为了做到这一点,您需要利用动态 SQL 根据您的输入值更改结果集。
有很多关于如何执行此操作的指南,但我将举例说明如何将其应用于您的脚本(添加了一些小的重新格式化)。为了 运行 它,我确实纠正了两个问题。 1.Add 子查询的别名 subquery_alias
2. 添加 GROUP BY
子句到聚合查询
DECLARE @TODAY DATE = CAST(GETDATE() AS DATE);
DECLARE @CUR_YR INT = YEAR(@TODAY);
DECLARE @PREV_YR INT = @CUR_YR-1;
DECLARE @Sql nvarchar(max) =
N'SELECT
*
FROM
(SELECT
BUS
,DET
,[STR]
,COALESCE(' + QUOTENAME(CAST(@PREV_YR AS nvarchar(10))) + N',0) AS PREV_YR
,COALESCE(' + QUOTENAME(CAST(@CUR_YR AS nvarchar(10))) + N',0) AS CUR_YR
FROM
(SELECT
YR
,BUS
,DET
,[STR]
,COUNT(ORD_ID_SE) AS ORDS
FROM
(<SUBQUERY>) AS subquery_alias
GROUP BY
YR
,BUS
,DET
,[STR]) AS T
PIVOT(SUM(ORDS) FOR YR IN (' + QUOTENAME(CAST(@PREV_YR AS nvarchar(10))) + N',' + QUOTENAME(CAST(@CUR_YR AS nvarchar(10))) + N')) AS pivot_table) AS F';
EXEC sys.sp_executesql @Sql;
也就是说,在这种情况下,您将数据透视表的输出重新别名分别为 PREV_YR
和 CUR_YR
。所以你真的不需要动态列名,如果你只打算以这种方式使用两个不同的变量,那么使用 SUM(CASE WHEN....END)
方法来转换它更有意义。像这样:
DECLARE @TODAY DATE = CAST(GETDATE() AS DATE);
DECLARE @CUR_YR INT = YEAR(@TODAY);
DECLARE @PREV_YR INT = @CUR_YR-1;
SELECT
BUS
,DET
,[STR]
,SUM(CASE WHEN YR = @PREV_YR THEN 1 ELSE 0 END) AS PREV_YR
,SUM(CASE WHEN YR = @CUR_YR THEN 1 ELSE 0 END) AS CUR_YR
FROM
(<SUBQUERY>) AS subquery_alias
GROUP BY
BUS
,DET
,[STR];
这样做可以完全避免动态 SQL 和更复杂的数据透视查询。