【问题标题】:how to create calculated pivot in sql如何在sql中创建计算的pivot
【发布时间】:2015-11-05 10:19:54
【问题描述】:

场景

XYZ 公司销售产品。公司通过检查完成整个订单需要多长时间来衡量他们的绩效。每个订单都有多个状态(例如:Open、Pending、Close)

他们计算每个状态的天数,以了解订单处于特定状态的天数。天数以两种不同的方式计算,工作日和日历日

请参考下表:

问题

如何将这个表格变成如下图所示的数据透视表?还如何添加到附加列,即每个工作日和工作日状态的总和。

期望的结果:

【问题讨论】:

    标签: sql sql-server pivot pivot-table


    【解决方案1】:

    你可以这样做:

    SELECT *
    FROM
    (
        SELECT 
            OrderID,
            OrderStatus + CountType AS StatusType,
            DayCount
        FROM CalendarTable     
        UNION ALL
        SELECT 
        OrderID,
        CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
        DayCount
        FROM CalendarTable
    ) AS t
    PIVOT
    (
       MAX(DayCount)
       For StatusType IN(OpenWorking,
                         OpenCalendar,
                         CloseWorking,
                         CloseCalendar,
                         PendingWorking,
                         PendingCalendar,
                         TotalWorking,
                         TotalCalendar)
    ) AS p;
    

    这会给你:


    如果你不想手动写下所有的状态,那么你可以动态地做:

    将@cols 声明为 NVARCHAR(MAX);

    DECLARE @query AS NVARCHAR(MAX);
    
    SELECT @cols = STUFF((SELECT distinct ',' +
                            QUOTENAME(StatusType)
                           FROM 
                           (
                                SELECT 
                                  OrderID,
                                  OrderStatus + CountType AS StatusType,
                                  DayCount
                                FROM CalendarTable     
                                UNION ALL
                                SELECT 
                                  OrderID,
                                  CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
                                  DayCount
                                FROM CalendarTable
                            ) AS t
                          FOR XML PATH(''), TYPE
                         ).value('.', 'NVARCHAR(MAX)') 
                            , 1, 1, '');
    
    
    SELECT @query = 'SELECT *
                    FROM
                    (
                        SELECT 
                          OrderID,
                          OrderStatus + CountType AS StatusType,
                          DayCount
                        FROM CalendarTable     
                        UNION ALL
                        SELECT 
                          OrderID,
                          CASE WHEN CountType = ''Working'' THEN ''TotalWorking'' ELSE ''TotalCalendar'' END,
                          DayCount
                        FROM CalendarTable
                    ) AS t
                    PIVOT
                    (
                       MAX(DayCount)
                       For StatusType IN(' + @cols + ')' +
                      ') p';
    
    execute(@query);
    

    更新:

    对于列名,您可以创建一个新变量@colnames 并用您想要的名称填充它。对于总计,您可以添加 WHERE 子句以仅获取活动和待定状态的总计。所以你的查询会是这样的:

    DECLARE @cols AS NVARCHAR(MAX);
    DECLARE @colnames AS NVARCHAR(MAX);
    DECLARE @query AS NVARCHAR(MAX);
    
    SELECT @cols = STUFF((SELECT distinct ',' +
                            QUOTENAME(StatusType)
                           FROM 
                           (
                                SELECT 
                                  OrderID,
                                  OrderStatus + CountType AS StatusType,
                                  DayCount
                                FROM CalendarTable     
                                UNION ALL
                                SELECT 
                                  OrderID,
                                  CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
                                  DayCount
                                FROM CalendarTable
                                WHERE OrderStatus IN('Active', 'Pending')
                            ) AS t
                          FOR XML PATH(''), TYPE
                         ).value('.', 'NVARCHAR(MAX)') 
                            , 1, 1, '');
    
    
    SELECT @colnames = STUFF((SELECT distinct ',' +
                            QUOTENAME(StatusType) + ' AS ' + QUOTENAME(StatusTypeName)
                           FROM 
                           (
                               SELECT 
                                    OrderID,
                                    OrderStatus + CountType AS StatusType,
                                    DayCount,
                                    OrderStatus + CASE WHEN CountType = 'Working' THEN  'WorkDays' ELSE 'CalDays' END AS StatusTypeName
                                FROM CalendarTable     
                                UNION ALL
                                SELECT 
                                  OrderID,
                                  CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
                                  DayCount,
                                  CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END
                                FROM CalendarTable
                                WHERE OrderStatus IN('Active', 'Pending')
                            ) AS t
                          FOR XML PATH(''), TYPE
                         ).value('.', 'NVARCHAR(MAX)') 
                            , 1, 1, '');
    
    SELECT @query = 'SELECT OrderID , ' + @colnames + '
                    FROM
                    (
                        SELECT 
                          OrderID,
                          OrderStatus + CountType AS StatusType,
                          DayCount
                        FROM CalendarTable     
                        UNION ALL
                        SELECT 
                          OrderID,
                          CASE WHEN CountType = ''Working'' THEN ''TotalWorking'' ELSE ''TotalCalendar'' END,
                          DayCount
                        FROM CalendarTable
                        WHERE OrderStatus IN(''Active'', ''Pending'')
                    ) AS t
                    PIVOT
                    (
                       SUM(DayCount)
                       For StatusType IN(' + @cols + ')' +
                      ') p';
    
    execute(@query);
    

    这会给你:


    更新

    如果您想在手动透视查询中添加 where 子句,您可以这样做:

    SELECT *
    FROM
    (
        SELECT 
            OrderID,
            OrderStatus + CountType AS StatusType,
            DayCount
        FROM CalendarTable     
        WHERE ...
        UNION ALL
        SELECT 
        OrderID,
        CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
        DayCount
        FROM CalendarTable
        WHERE ...
    ) AS t
    PIVOT
    (
       MAX(DayCount)
       For StatusType IN(OpenWorking,
                         OpenCalendar,
                         CloseWorking,
                         CloseCalendar,
                         PendingWorking,
                         PendingCalendar,
                         TotalWorking,
                         TotalCalendar)
    ) AS p;
    

    【讨论】:

    • 谢谢Mahmoud,如何获得额外的两列呢?所有状态的工作日和工作日总数。正如我在问题中提到的那样?
    • @Lokapedia - 对不起,我的错,我忘记了。我更新了我的答案,请查找我更新后的查询。
    • 谢谢 Mahmoud,我会尽快通知你结果 :) 非常感谢
    • 我测试了手动脚本,它给了我想要的结果。但是我可以问你以下两件事吗? (1) 如何更改列名? (2)总工作日和总日历天的最后两列..我只想要“活动”和“待定”状态天数的总和..而不是任何其他状态
    • 谢谢 mate.. 动态查询工作正常,但我在哪里可以在原始手动数据透视表脚本中添加 WHERE 子句以获得总共只有两个状态?
    猜你喜欢
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-28
    • 2021-12-08
    相关资源
    最近更新 更多