【问题标题】:PIVOT Not Working on a DENSE_RANK ColumnPIVOT 不适用于 DENSE_RANK 列
【发布时间】:2015-07-10 22:19:33
【问题描述】:

我正在编写一份报告,提供每日运营数据的每周总计。报告顶部将有 8 周作为列。每排都是一辆车。诸如日期范围之类的变量最终将变为动态的,在我开发它的同时,这现在有点硬编码。 8 周可以保持硬编码。

这很好用:

SELECT vehicle, [2] AS W2, [3] AS W3, [4] AS W4, [5] AS W5, [6] AS W6, [7] AS W7, [8] AS W8, [9] AS W9
FROM
(
    SELECT 
        Vehicle
        ,datepart(week,recordedoneffective) as WeekNum
        ,sum(totalweight ) AS TotalWeight
    from vw_visit_details v, vw_food_stats f
    where v.VisitId = f.VisitId
    and recordedoneffective between '2015-01-05' and '2015-03-01'
    group by vehicle,datepart(week,recordedoneffective)
) src
PIVOT
(
    SUM(TotalWeight) 
    FOR WeekNum IN ([2],[3],[4],[5],[6],[7],[8],[9])
    ) pvt
ORDER BY pvt.vehicle

结果:

Vehicle             W2      W3      W4      W5      W6      W7      W8      W9
Apples - D44CW      2603    7840    3200    1540    3516    2828    3217    3911
Bananas - 664SEC    4063    5004    5935    8734    8333    8663    4591    7807
Capsicum - YPD094   4699    6191    6423    4560    5742    7004    7204    5347

但是,由于 WeekNum 取决于指定的日期,因此我想将 WeekNum 转换为一个排名,该排名始终保持不变。为此,我使用了 DENSE_RANK,因此将 8 周数字(在本例中为 2-9)的范围转换为数字 1-8。

SELECT vehicle, [1] AS W1, [2] AS W2, [3] AS W3, [4] AS W4, [5] AS W5, [6] AS W6, [7] AS W7, [8] AS W8
FROM
(
        select 
        Vehicle
        ,datepart(week,recordedoneffective) as WeekNum
        ,dense_rank() OVER ( ORDER BY datepart    (week,recordedoneffective) ) AS WeekSeq
        ,sum(totalweight ) AS TotalWeight
    from vw_visit_details v, vw_food_stats f
    where v.VisitId = f.VisitId
    and recordedoneffective between '2015-01-05' and '2015-03-01'
    group by vehicle,   datepart(week,recordedoneffective)
) src
PIVOT
(
    SUM(TotalWeight) 
    FOR WeekSeq IN ([1],[2],[3],[4],[5],[6],[7],[8])
    ) pvt
ORDER BY pvt.vehicle

请注意,列名现在是 1-8 而不是 2-9,并且透视列已更改。然而,现在其余集没有正确旋转。

Vehicle         W1      W2      W3      W4      W5      W6      W7      W8
Apples - D44CW  2603    NULL    NULL    NULL    NULL    NULL    NULL    NULL
Apples - D44CW  NULL    7840    NULL    NULL    NULL    NULL    NULL    NULL
Apples - D44CW  NULL    NULL    3200    NULL    NULL    NULL    NULL    NULL
Apples - D44CW  NULL    NULL    NULL    1540    NULL    NULL    NULL    NULL
Apples - D44CW  NULL    NULL    NULL    NULL    3516    NULL    NULL    NULL
Apples - D44CW  NULL    NULL    NULL    NULL    NULL    2828    NULL    NULL
Apples - D44CW  NULL    NULL    NULL    NULL    NULL    NULL    3217    NULL
Apples - D44CW  NULL    NULL    NULL    NULL    NULL    NULL    NULL    3911
Bananas - 664SEC    4063    NULL    NULL    NULL    NULL    NULL    NULL    NULL

所以基本上,枢轴不再旋转。两个查询的内部 src 结果集完全相同(除了添加新列) - 返回相同数量的结果,没有新的空值等。

我想也许我需要GROUP BY dense_rank() OVER ( ORDER BY datepart(week,recordedoneffective) ) 列,但你不能按此分组。还认为我应该尝试删除现在未使用的 datepart(week,recordedoneffective) 计算 col - 事实证明,为了在 OVER 子句中使用它,仍然必须有它。

为什么这个支点不起作用?大概我可以使用更动态的 SQL 来解决这个问题,但我看不出为什么这不起作用。

【问题讨论】:

    标签: sql sql-server pivot


    【解决方案1】:

    您的 CTE 有四列:Vehicle、WeekNum、WeekSeq 和 TotalWeight。 PIVOT 子句引用最后两个;这意味着枢轴的隐式分组基于前两列。因此,对于 Vehicle 和 WeekNum 的每个独特组合,您都会得到一行。这在您的结果集中并不清楚,因为您没有选择 WeekNum。

    在您的情况下,答案很简单 - 从 CTE 中删除 WeekNum,因为您实际上并没有使用它。

    【讨论】:

    • 谢谢,这解释了我尚未从@Hart CO 的回答中得到的原因。但是我确实尝试删除 col 但由于语法原因而无法删除(所以它在技术上并不完全正确)。我没有意识到(但现在看来很明显)枢轴具有隐式分组。
    • 更新:我在尝试删除列时做错了事。我现在让它在没有来自其他答案的额外 CTE 层的情况下工作。谢谢!
    【解决方案2】:

    由于您没有在PIVOT 输出中使用WeekNum,因此您希望在使用DENSE_RANK() 之前先执行GROUP BY

    ;WITH cte AS (SELECT Vehicle
                        ,datepart(week,recordedoneffective) as WeekNum                  
                        ,sum(totalweight ) AS TotalWeight
                  FROM vw_visit_details v, vw_food_stats f
                  WHERE v.VisitId = f.VisitId
                    and recordedoneffective between '2015-01-05' and '2015-03-01'
                  GROUP BY vehicle,   datepart(week,recordedoneffective)
                  )
         ,cte2 AS (SELECT  vehicle 
                          ,dense_rank() OVER ( ORDER BY WeekNum ) AS WeekSeq 
                          ,TotalWeight
                   FROM cte
                   )
    SELECT vehicle, [1] AS W1, [2] AS W2, [3] AS W3, [4] AS W4, [5] AS W5, [6] AS W6, [7] AS W7, [8] AS W8
    FROM cte2
    PIVOT (SUM(TotalWeight) FOR WeekSeq IN ([1],[2],[3],[4],[5],[6],[7],[8])) pvt
    ORDER BY pvt.vehicle
    

    【讨论】:

    • 太棒了,谢谢!注意我必须在cte2 中更改为dense_rank OVER (ORDER BY WeekNum) AS WeekSeq 只是为了使语法有效(因为它现在是cte 中的命名列而不是当前语句中的计算列)。我不完全理解为什么需要额外的GROUP BY - 不应该忽略额外未使用的列吗?
    • @Visser 如果您在原始SELECT 中包含WeekNum 并运行,那么推理可能最容易理解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 2013-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多