You can use conditional aggregation and two levels of aggregation:
select yyyy, mm,
count(*) as num_customers,
sum(num_orders) as num_orders,
sum(case when num_orders = 1 then 1 else 0 end) as cnt_1_order,
sum(case when num_orders = 2 then 1 else 0 end) as cnt_2_order,
sum(case when num_orders = 3 then 1 else 0 end) as cnt_3_order
from (select o.customer_id,
year(o.order_date) as yyyy, month(o.order_date) as mm,
count(*) as num_orders
from orders o
group by o.customer_id
) o
group by yyyy, mm
order by yyyy, mm;
You can modify the sum()
s in the outer query to have the specific values or ranges that you care about.
If you want 0
s, just subtract the total number of customers from the number that ordered in each month. That would be something like this:
select yyyy, mm,
count(*) as num_customers_with_order,
c.cnt - num_customers as customers_no_orders,
sum(num_orders) as num_orders,
sum(case when num_orders = 1 then 1 else 0 end) as cnt_1_order,
sum(case when num_orders = 2 then 1 else 0 end) as cnt_2_order,
sum(case when num_orders = 3 then 1 else 0 end) as cnt_3_order
from (select o.customer_id,
year(o.order_date) as yyyy, month(o.order_date) as mm,
count(*) as num_orders
from orders o
group by o.customer_id
) o cross join
(select count(*) as cnt from customers) c
group by yyyy, mm
order by yyyy, mm;
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…