I have a query that I use to generate the total order amount for customers and group them into columns by month alongside another column that represents total order amount.
Here's the schema:
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
For convenience, I've added the create table statements:
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));
Here's the query I'm using:
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;
How can I get rid of that ugly case statement? There MUST be a more elegant way that rolls up these queries over a certain time slice. I tried to use window functions, but I've failed miserably. Any assistance would be appreciated!
I'm using postgresql 9.1
This is not simpler but is more scalable:
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)