Search code examples
mysqlsqldateaggregate-functionsbucket

Uneven automated buckets/bins in SQL


ads Table:
-one row per ad per day

date    |   ad_id  | account_id | spend  
2018-05-01   123     1101        100  
2018-05-02   123     1101        125  
2018-05-03   124     1101        150  
2018-05-04   124     1101        150    
2018-05-04   125     1105        150  
2018-05-04   126     1105        150  
2018-05-04   123     1101        150  
2018-01-01   123     1101        150              

I am trying to create a histogram to show the how much advertisers have spent in last 7 days. I want the first bucket to be $10-999.99 and others to be $1000-1999.99,$2000-2999.99 etc but this I want to achieve through automation not by manually mentioning buckets through case function.

My current code does well in creating even automated buckets:

select CONCAT(1000*FLOOR(last_7_days_spend/1000), "-", 1000*FLOOR(last_7_days_spend/1000)+999.99) "spend($)" , count(*) "frequency"
from
(select account_id, sum(spend) "last_7_days_spend"
from fb_ads
where date between date_sub(curdate(), interval 7 day) and date_sub(curdate(), interval 1 day)
group by account_id) as abc
group by 1
order by 1;

and it returns this:

spend       |   frequency
0-999.99        2  
2000-2999.99    1

But want to write some similar kind of query which should filter out records and start from $10-999.99 instead of $0.00-999.99. Desired output:

spend       |   frequency  
10-999.99       2  
2000-2999.99    1

Solution

  • You'll need to use a CASE expression to define the first bucket, but you can automate the other buckets within that expression. Note that if you don't want a bucket for a spend of less than $10, you'll need to filter those values out:

    SELECT
        CASE WHEN last_7_days_spend < 1000 THEN '10-999.99'
             ELSE CONCAT(1000*FLOOR(last_7_days_spend/1000), "-", 1000*FLOOR(last_7_days_spend/1000)+999.99)
        END AS `spend($)`,
        COUNT(*) AS `frequency`
    FROM (
        SELECT account_id, SUM(spend) AS `last_7_days_spend`
        FROM fb_ads
        WHERE date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND DATE_SUB(CURDATE(), INTERVAL 1 DAY)
        GROUP BY account_id
    ) as abc
    WHERE last_7_days_spend >= 10
    GROUP BY 1
    ORDER BY 1
    

    Small demo on db-fiddle