如何在 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_YRCUR_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 和更复杂的数据透视查询。