Home > front end >  How to create a Postgres query that will generate a series with calculated values
How to create a Postgres query that will generate a series with calculated values

Time:08-10

I have this query that I use to calculate returning customers (with more than one order)

SELECT COUNT(*)
  FROM (SELECT customer_id, COUNT(*) as order_count
        FROM orders
        WHERE shop_id = #{shop_id}
          AND orders.active = true
          AND orders.created_at >= '#{from}'
          AND orders.created_at < '#{to}'
        GROUP BY customer_id
        HAVING COUNT(orders) > 1
        ORDER BY order_count) src;

And if I want new customers (that have only one order) I simply change this line:

HAVING COUNT(orders) = 1

Now, how can I generate a series between 2 given dates that will give me the number of new and returning customers for each day between the dates?

Expected result:

date new returning
2022-01-01 2 3
2022-01-02 5 9

I have tried this but doesn't work at all (error syntax near from is the error I'm getting) and I'm not sure how to fix. Ideas?

    select *, return_customers
      from (select created_at, count(*) as order_count
      from orders
      where shop_id = 43
        and created_at >= '2022-07-01'
        and created_at < '2022-07-10'
      group by customer_id
      having count(orders) > 1
      order by order_count) as return_customers from generate_series(timestamp '2007-01', timestamp '2022-07-11', interval '1 day')
    as g(created_at)
    left join (
        select created_at::date,
                count(*) as order_count
                from orders
                where shop_id 43
                and created_at >= '2022-07-01'
                and created_at < '2022-07-10'
                group by customer_id
                having count(orders) > 1
                order by order_count
                group by 1) o using (created_at)) sub
order by created_at desc;

CodePudding user response:

This is based on your initial query w/o the having clause and conditional counts with filter. order by in src is redundant too.

SELECT src.order_date as "date", 
       COUNT(*) filter (where order_count > 1) as "returning",
       COUNT(*) filter (where order_count = 1) as "new"
FROM 
(
  SELECT date_trunc('day', o.created_at)::date as order_date, 
         COUNT(*) as order_count
  FROM orders o
  WHERE o.shop_id = #{shop_id}
    AND o.active
    AND o.created_at >= '#{from}'
    AND o.created_at < '#{to}'
  GROUP BY o.customer_id, order_date
) as src
group by order_date;
  • Related