没有存储过程、临时表、只有一个查询,以及给定日期列索引的高效执行计划:
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() 这样的东西,所以你必须在你的应用程序中处理它或者尝试this clever hack。