如何将此不可伸缩的查询转换为动态列的窗口函数?

时间:2013-02-05 09:36:16

标签: sql postgresql rollup

我有一个查询,用于生成客户的总订单金额,并按月将其分组到另一列,代表总订单金额。

这是架构:

temp=# \d+ customers;
                          Table "pg_temp_2.customers"
   Column   |            Type             | Modifiers | Storage  | Description 
------------+-----------------------------+-----------+----------+-------------
 id         | integer                     | not null  | plain    | 
 created_at | timestamp without time zone |           | plain    | 
 name       | text                        |           | extended | 
Indexes:
    "customers_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "orders" CONSTRAINT "orders_customer_id_fkey" FOREIGN KEY (customer_id) REFERENCES customers(id)
Has OIDs: no


                             Table "pg_temp_2.orders"
   Column    |            Type             |   Modifiers   | Storage | Description 
-------------+-----------------------------+---------------+---------+-------------
 id          | integer                     | not null      | plain   | 
 created_at  | timestamp without time zone | default now() | plain   | 
 customer_id | integer                     |               | plain   | 
 amount      | integer                     |               | plain   | 
Indexes:
    "orders_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "orders_customer_id_fkey" FOREIGN KEY (customer_id) REFERENCES customers(id)
Has OIDs: no

为方便起见,我添加了create table语句:

create temporary table customers ( id integer primary key, created_at timestamp without time zone, name text); 

create temporary table orders ( id integer primary key, created_at timestamp without time zone, customer_id integer references customers(id));

这是我正在使用的查询:

SELECT
  c.name,
  sum(o.amount),
  CAST(SUM(
   CASE
        WHEN date_trunc('month', o.created_at) BETWEEN '2012-10-01' AND ('2012-11-01'::date - '1 day'::interval)
        THEN o.amount
        ELSE 0
   END
   ) / 100.0 AS MONEY) october2012,
   CAST(SUM(
   CASE
        WHEN date_trunc('month', o.created_at) BETWEEN '2012-11-01' AND ('2012-12-01'::date - '1 day'::interval)
        THEN o.amount
        ELSE 0
   END
   ) / 100.0 AS MONEY) as november2012
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
WHERE o.created_at >= '01 October 2012'
  AND o.created_At < '01 December 2012'
GROUP BY
 c.name
ORDER BY
  october2012 desc;

如何摆脱丑陋的案件陈述? 必须是一种更优雅的方式,可以在某个时间片上汇总这些查询。我试图使用窗口函数,但我失败了。任何帮助将不胜感激!

我正在使用postgresql 9.1

1 个答案:

答案 0 :(得分:1)

这并不简单,但可扩展性更高:

select *
from crosstab($$
    with orders as (
        select
            customer_id,
            date_trunc('month', created_at) created_at,
            cast(amount / 100.0 as money) amount
        from orders
        where
            created_at >= '2012-10-01'
            and created_at < '2012-12-01'
    ), months as (
        select
            c.name,
            to_char(o.created_at, 'YYYY-MM') created_at,
            sum(o.amount) amount
        from
            orders o
            inner join
            customers c on c.id = o.customer_id
        group by 1, 2
    )
    select name, created_at, amount
    from months
    union all
    select name, 'Total', sum(amount)
    from months
    group by 1, 2
    order by 1, 2
    $$, $$
    select distinct to_char(created_at, 'YYYY-MM')
    from orders
    where
        created_at >= '2012-10-01'
        and created_at < '2012-12-01'
    union select 'Total'
    order by 1
    $$
) as (name text, "2012-10" money, "2012-11" money, "Total" money)