Relyn C
Relyn C

Reputation: 3

Sql - Query to calculate no. of buyers monthly

i would really need your help in creating a query where i can show the number of buyers who made more than 1 order in a month for the period January 2017 to December 2017.

Also, the no. of these buyers that didn't order again within the next 12 months.

Below is sample my data.

Date CID (customer id) OrderNo
2017-02-03 0:23 924832 000023149
2017-02-05 0:11 924162 000092384
2017-07-01 0:53 914861 000023182
2017-08-09 0:42 924832 000021219

Output should be like this

Month Buyers with >1 order Buyers that didnt order for the next 12M
2017-01-01 122 92
2017-02-01 74 24
2017-03-01 216 107

Upvotes: 0

Views: 170

Answers (3)

dnoeth
dnoeth

Reputation: 60462

This should to match your description:

with cte as
 (
   select
      ca.ym
     ,cid
     -- flag buyers who made more than 1 order in a month
     ,case when count(*) > 1 then 1 else 0 end as multi_buy

     -- flag buyers that didn't order again within the next 12 months
     ,case when LEAD(ym,1,ym) -- month of next order
                over (partition by CID
                      order by ym) < DATEADD(month, 13, ym)
           then 0 
           else 1
      end as no_buy_within_range
   from orders
   CROSS APPLY 
    ( -- truncate the OrderDate to the 1st of month
      SELECT convert(Date, DATEADD(month, DATEDIFF(month, 0, OrderDate), 0)) as ym
    ) as ca
   group by
      ym
     ,cid
 )
select 
   ym
  ,sum(multi_buy)
  ,sum(no_buy_within_range)
from cte
group by ym
order by ym

Upvotes: 1

Element Zero
Element Zero

Reputation: 1751

I made this setup here http://sqlfiddle.com/#!18/090ac/5

;WITH CTE AS
(
    select  CAST(DATEADD(month, DATEDIFF(month, 0, OrderDate), 0) as DATE) AS OrderMonth,
            CID,count(OrderNo) as OrderCount
    from Orders
    group by CAST(DATEADD(month, DATEDIFF(month, 0, OrderDate), 0) as DATE), CID
)
select q1.OrderMonth, q1.[MultipleOrderBuyers], q2.OrderedOverNext12Mo 
from
(
    select  OrderMonth, 
            SUM(CASE WHEN OrderCount > 1 THEN 1 ELSE 0 END) as [MultipleOrderBuyers]
    from CTE
    group by OrderMonth
)q1
LEFT OUTER JOIN
(
    select OrderMonth, SUM(OrderedOverNext12Mo) as OrderedOverNext12Mo
    from
    (
        select OrderMonth, CID, 
                (   select CASE WHEN SUM(OrderCount) > 1 THEN 1 ELSE 0 END 
                    from CTE 
                    where OrderMonth BETWEEN DATEADD(month,1,c1.OrderMonth) AND DATEADD(month,13,c1.OrderMonth)
                ) as OrderedOverNext12Mo
        from CTE c1
        group by OrderMonth, CID
    )c
    group by OrderMonth
)q2 on (q1.OrderMonth = q2.OrderMonth)

Upvotes: 0

Artiom Rombakh
Artiom Rombakh

Reputation: 1

If I understood correctly, the table with sample data is the table of Orders, which means that one row is one order with unique OrderNo.

If so, then the following query would have to work:

SELECT d.[MONTH], COUNT(distinct d.[Buyers with >1 order]) [Buyers with >1 order],
    COUNT(distinct d.[Buyers that didnt order for the next 12M]) [Buyers that didnt order for the next 12M]
FROM
(
    select c.[MONTH], c.[Date], c.CID, c.OrderNo,
            c.[Buyers with >1 order],
            case when c.[Buyers with >1 order] is not null and
                datediff(MONTH, c.[Date], LEAD(c.[Date]) over (partition by c.[CID] order by c.[Date])) > 12
                THEN C.CID END [Buyers that didnt order for the next 12M]
    from (
            SELECT b.[MONTH], b.[Date], b.CID, b.OrderNo,
                    case when COUNT(*) over (partition by b.[MONTH], b.CID) > 1 then b.CID end [Buyers with >1 order]
            FROM (
                    SELECT convert(date, DATEADD(month, DATEDIFF(month, 0, a.Date), 0)) [MONTH],
                            A.[Date], A.CID, A.OrderNo
                    FROM Orders a
                ) b
        ) c
) d
WHERE D.[MONTH] BETWEEN '2017-01-01' AND '2017-12-01'
GROUP BY d.[MONTH]

UPD: I just noted your comment clarifying the meaning of "Buyers that didn't order for the next 12M": "if you ordered last January 2017, you shouldnt have any orders from Feb 2017 to Feb 2018"

For such cases, the part of the query

datediff(MONTH, c.[Date], LEAD(c.[Date]) over (partition by c.[CID] order by c.[Date])) > 12

ought to be altered to

DATEADD(DAY,1,EOMONTH(DATEADD(MONTH,13,C.[DATE]))) <= LEAD(c.[Date]) over (partition by c.[CID] order by c.[Date])

Then the full query would look like:

SELECT d.[MONTH], COUNT(distinct d.[Buyers with >1 order]) [Buyers with >1 order],
COUNT(distinct d.[Buyers that didnt order for the next 12M]) [Buyers that didnt order for the next 12M]
FROM
(
select c.[MONTH], c.[Date], c.CID, c.OrderNo,
        c.[Buyers with >1 order],
        case when c.[Buyers with >1 order] is not null and
            datediff(MONTH, c.[Date], LEAD(c.[Date]) over (partition by c.[CID] order by c.[Date])) > 12
            THEN C.CID END [Buyers that didnt order for the next 12M]
from (
        SELECT b.[MONTH], b.[Date], b.CID, b.OrderNo,
                case when COUNT(*) over (partition by b.[MONTH], b.CID) > 1 then b.CID end [Buyers with >1 order]
        FROM (
                SELECT convert(date, DATEADD(month, DATEDIFF(month, 0, a.Date), 0)) [MONTH],
                        A.[Date], A.CID, A.OrderNo
                FROM Orders a
            ) b
    ) c
) d

WHERE D.[MONTH] BETWEEN '2017-01-01' AND '2017-12-01'
GROUP BY d.[MONTH]

Upvotes: 0

Related Questions