没有存储过程、临时表、只有一个查询,以及给定日期列索引的高效执行计划:
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);
除了这个咒语之外,这里发生的事情应该很明显:
floor(dateDiff('2012-12-31', dateStampColumn) / 30)
该表达式出现多次,其计算结果为 30 天前的周期dateStampColumn
数。dateDiff
返回以天为单位的差值,将其除以 30 以在 30 天的周期内得到它,然后将其全部输入以floor()
将其舍入为整数。一旦我们有了这个数字,我们就可以GROUP BY
了,然后我们做一些数学运算,将这个数字转换回该时期的开始和结束日期。
如果您愿意,请替换'2012-12-31'
为。now()
以下是一些示例数据:
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');
结果:
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
期间端点包括在内。
在SQL Fiddle中玩这个。
有一点潜在的愚蠢之处在于,任何匹配行为零的 30 天期间都不会包含在结果中。如果您可以将其与一个时期表结合起来,则可以将其消除。然而,MySQL 没有像 PostgreSQL 的generate_series()这样的东西,所以你必须在你的应用程序中处理它或者尝试这个聪明的 hack。