简体   繁体   中英

Sql Statement to get customer who didn't place any order monthly (with month also showed)

I have a customer table and an orders table In this case I would like to have a monthly report about how many customer didn't place any order Like this Month Number of inactive customer

I have written a query like this:

 SELECT Count (distinct Persons.id), Month (Orders.OrderDate) FROM Persons LEFT JOIN Orders ON Persons.id = Orders.Person_id WHERE Orders.Person_id IS NULL; Group By Month (Orders.OrderDate)

Due to the condition IS NULL, then the result of Month (OrderDate) is also NULL How should I let the month also be displayed? I am using tsql

If you have at least one order per months in table orders , then you can cross join the persons with the distinct months, and filter on inactive clients with a not exists condition:

select d.ordermonth, count(*) no_inactive_clients
from persons p
cross join (select distinct datefromparts(year(orderdate), month(orderdate), 1) ordermonth from orders) d
where not exists (
    select 1 
    from orders o 
    where 
        o.orderdate >= d.ordermonth 
        and o.orderdate < dateadd(month, 1 d.ordermonth)
        and o.person_id = p.id
)
group by d.ordermonth

If there are months without any order, they will not show in the resultset though. In that case, you need a calendar table instead; you can store it permanently, or generate it with a recursice CTE or a tally.

Generate a list of dates. One method is a recursive CTE to generate the list. Then count the total number of people and subtract the ones who have not made an order:

with dates as (
      select convert(date, '2019-01-01') as dte
      union all
      select dateadd(month, 1, dte)
      from dates
      where dte < getdate()
     )
select d.dte, p.total - count(distinct o.personid)
from dates d left join
     orders o
     on o.orderdate >= d.dte and
        o.orderdate < dateadd(month, 1, d.dte) cross join
     (select count(*) as total from person) p
group by d.dte
order by d.dte;

Actually, if someone places an order very month, then there is no need for the CTE:

select datefromparts(year(o.orderdate), month(o.orderdate), 1) as yyyymm,
       p.total - count(distinct o.person_id)
from orders o cross join
     (select count(*) as total from person) p
group by datefromparts(year(o.orderdate), month(o.orderdate), 1), p.total
order by min(orderdate);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM