使用交叉表创建数据透视表的正确方法

时间:2013-01-30 16:54:50

标签: sql postgresql heroku case crosstab

如何使用交叉表将以下查询转换为数据透视表?

select (SUM(CASE WHEN added_customer=false
        THEN 1
        ELSE 0 
        END)) AS CUSTOMERS_NOT_ADDED, (SUM(CASE WHEN added_customer=true
        THEN 1
        ELSE 0 
        END)) AS CUSTOMERS_ADDED, 

        (select (SUM(CASE WHEN added_sales_order=false
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS SALES_ORDER_NOT_ADDED, 

        (select (SUM(CASE WHEN added_sales_order=true
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS SALES_ORDER_ADDED,


        (select (SUM(CASE WHEN added_fulfillment=false
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS ITEM_FULFILLMENT_NOT_ADDED, 

        (select (SUM(CASE WHEN added_fulfillment=true
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS ITEM_FULFILLMENT_ADDED,


        (select (SUM(CASE WHEN added_invoice=false
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS INVOICE_NOT_ADDED, 

        (select (SUM(CASE WHEN added_invoice=true
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS INVOICE_ADDED,

        (select (SUM(CASE WHEN added_ra=false
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS RA_NOT_ADDED, 

        (select (SUM(CASE WHEN added_ra=true
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS RA_ADDED,

        (select (SUM(CASE WHEN added_credit_memo=false
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS CREDIT_MEMO_NOT_ADDED, 

        (select (SUM(CASE WHEN added_credit_memo=true
                THEN 1
                ELSE 0 
                END))  
        FROM shipments_data
        ) AS CREDIT_MEMO_ADDED

FROM shipments_data;

此查询以标准行格式提供数据,但我希望以下列格式将其显示为数据透视表:

                    Added    Not_Added
Customers             100            0
Sales Orders           50           50
Item Fulfillemnts       0          100
Invoices                0          100
...

我正在使用运行v9.1.6的Heroku PostgreSQL

另外,我不确定我的上述查询是否可以优化,或者这是不是很糟糕。如果可以优化/改进,我很乐意学习如何。

1 个答案:

答案 0 :(得分:2)

提供crosstab()的{​​{3}}模块可用于9.1(与千禧年这一侧的任何其他版本一样)。 Heroku不允许你安装额外的模块吗?你试过了吗?

CREATE EXTENSION tablefunc;

有关如何使用它的示例,请参阅tablefunc或此相关问题:
the manual

PostgreSQL Crosstab Query - 关于SO的例子,有几个很好的答案。

为了让您入门(就像大多数情况一样..)使用这个大大简化和重新组织的查询作为crosstab()调用的基础:

SELECT 'added'::text AS col
      ,SUM(CASE WHEN added_customer    THEN 1 ELSE 0 END) AS customers
      ,SUM(CASE WHEN added_sales_order THEN 1 ELSE 0 END) AS sales_order
      ,SUM(CASE WHEN added_fulfillment THEN 1 ELSE 0 END) AS item_fulfillment
      ,SUM(CASE WHEN added_invoice     THEN 1 ELSE 0 END) AS invoice
      ,SUM(CASE WHEN added_ra          THEN 1 ELSE 0 END) AS ra
      ,SUM(CASE WHEN added_credit_memo THEN 1 ELSE 0 END) AS credit_memo
FROM   shipments_data

UNION ALL
SELECT 'not_added' AS col
      ,SUM(CASE WHEN NOT added_customer    THEN 1 ELSE 0 END) AS customers
      ,SUM(CASE WHEN NOT added_sales_order THEN 1 ELSE 0 END) AS sales_order
      ,SUM(CASE WHEN NOT added_fulfillment THEN 1 ELSE 0 END) AS item_fulfillment
      ,SUM(CASE WHEN NOT added_invoice     THEN 1 ELSE 0 END) AS invoice
      ,SUM(CASE WHEN NOT added_ra          THEN 1 ELSE 0 END) AS ra
      ,SUM(CASE WHEN NOT added_credit_memo THEN 1 ELSE 0 END) AS credit_memo
FROM   shipments_data;

如果您的列已定义NOT NULL,则可以进一步简化CASE表达式。

如果效果至关重要,您可以在CTE中的单次扫描中获取所有聚合 ,并在下一步中将值拆分为两行。

WITH x AS (
   SELECT count(NULLIF(added_customer, FALSE)) AS customers
         ,sum(added_sales_order::int)          AS sales_order
          ...
         ,count(NULLIF(added_customer, TRUE))  AS not_customers
         ,sum((NOT added_sales_order)::int)    AS not_sales_order
          ...
   FROM   shipments_data
   )
SELECT 'added'::text AS col, customers, sales_order, ... FROM x
UNION  ALL
SELECT 'not_added', not_customers, not_sales_order, ...  FROM x;

我还演示了构建聚合的两种替代方法 - 都建立在假设所有列都是 boolean NOT NULL 的基础上。两种选择都在语法上更短,但不会更快。在之前的测试中,所有三种方法的表现基本相同。