There are no stored procedures, temporary tables, only one query and an effective execution plan with an index in the date column:
select subdate( '2012-12-31', floor(dateDiff('2012-12-31', dateStampColumn) / 30) * 30 + 30 - 1 ) as "period starting", subdate( '2012-12-31', floor(dateDiff('2012-12-31', dateStampColumn) / 30) * 30 ) as "period ending", count(*) from YOURTABLE group by floor(dateDiff('2012-12-31', dateStampColumn) / 30);
It should be pretty obvious what happens besides this spell:
floor(dateDiff('2012-12-31', dateStampColumn) / 30)
This expression appears several times, and it is evaluated by the number of 30-day periods ago by dateStampColumn . dateDiff returns the difference in days, divides it by 30 to get it in 30-day periods, and pass it to floor() to round it to the nearest integer. As soon as we get this number, we can GROUP BY it, and then we will do some math to translate this number back to the start and end dates of the period.
Replace '2012-12-31' with now() if you want. Here are some sample data:
CREATE TABLE YOURTABLE (`Id` int, `dateStampColumn` datetime); INSERT INTO YOURTABLE (`Id`, `dateStampColumn`) VALUES (1, '2012-10-15 02:00:00'), (1, '2012-10-17 02:00:00'), (1, '2012-10-30 02:00:00'), (1, '2012-10-31 02:00:00'), (1, '2012-11-01 02:00:00'), (1, '2012-11-02 02:00:00'), (1, '2012-11-18 02:00:00'), (1, '2012-11-19 02:00:00'), (1, '2012-11-21 02:00:00'), (1, '2012-11-25 02:00:00'), (1, '2012-11-25 02:00:00'), (1, '2012-11-26 02:00:00'), (1, '2012-11-26 02:00:00'), (1, '2012-11-24 02:00:00'), (1, '2012-11-23 02:00:00'), (1, '2012-11-28 02:00:00'), (1, '2012-11-29 02:00:00'), (1, '2012-11-30 02:00:00'), (1, '2012-12-01 02:00:00'), (1, '2012-12-02 02:00:00'), (1, '2012-12-15 02:00:00'), (1, '2012-12-17 02:00:00'), (1, '2012-12-18 02:00:00'), (1, '2012-12-19 02:00:00'), (1, '2012-12-21 02:00:00'), (1, '2012-12-25 02:00:00'), (1, '2012-12-25 02:00:00'), (1, '2012-12-26 02:00:00'), (1, '2012-12-26 02:00:00'), (1, '2012-12-24 02:00:00'), (1, '2012-12-23 02:00:00'), (1, '2012-12-31 02:00:00'), (1, '2012-12-30 02:00:00'), (1, '2012-12-28 02:00:00'), (1, '2012-12-28 02:00:00'), (1, '2012-12-30 02:00:00');
And the result:
period starting period ending count(*) 2012-12-02 2012-12-31 17 2012-11-02 2012-12-01 14 2012-10-03 2012-11-01 5
Period terms
included.
Play with this in SQL Fiddle .
There's a bit of potential dumbness in that any 30-day period with zero matching lines will not be included in the result. If you could join this against the period table, that could be fixed. However, MySQL has nothing like PostgreSQL generate_series () , so you have to deal with it in your application or try this smart hack .