mysql中有没有办法按月分组,但是有自定义的开始日期。
假设我想按月计算登录数,但条件是月份在用户注册时开始。
例如,1月30日注册用户 A ,1月15日用户 B
我应该将登录分组如下:
* User A: January 30th - February 28th, March 1st - March 30th, March 31 - April 30 and so on and so forth
* User B: January 15th - February 14th, February 15th - March 14th and so on and so forth
我想我需要使用类似DATE_ADD('2013-01-30', INTERVAL 1 MONTH);
的内容,但我似乎无法找到进行分组的方法。
更新
@GarethD:你是对的,这是一个错字
一般情况下,月份应该从下个月的同一天或下个月的最后一天开始,以防第一个月不可能,所以如果您在第31天注册,则月份将在第30天开始没有31天和2月的最后一天28或29的月份
示例:
鉴于此
id 1 registered on 2012-12-16
id 2 registered on 2013-01-29
和下表
+----+------------+
| id | date |
+----+------------+
| 1 | 2013-01-15 |
| 1 | 2013-01-16 |
| 1 | 2013-01-17 |
| 1 | 2013-01-17 |
| 2 | 2013-03-20 |
| 2 | 2013-03-21 |
| 2 | 2013-03-28 |
| 2 | 2013-03-29 |
| 2 | 2013-03-30 |
+----+------------+
结果应该是
+----+----------------------------+-------+
| id | range | count |
+----+----------------------------+-------+
| 1 | 2012-12-16, 2013-01-15 | 1 |
| 1 | 2013-01-16, 2013-02-15 | 3 |
| 2 | 2013-02-2[8|9], 2013-03-28 | 3 |
| 2 | 2013-03-29, 2013-04-28 | 2 |
+----+----------------------------+-------+
我希望现在的意图更加明确。
答案 0 :(得分:4)
对于以下我假设你已经有一个数字表,如果你没有数字表,那么我建议你先制作一个,但是如果你不想那么你就可以{{3 }}
您可以通过将您的用户ID和注册日期与您的号码表交叉加入来获取所有边界的列表:
SELECT u.ID,
DATE_ADD(RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(RegisteredDate, INTERVAL n.Number + 1 MONTH) PeriodEnd
FROM User u
CROSS JOIN Numbers n;
这给出了一个表格:
ID PERIODSTART PERIODEND
1 2012-12-16 2012-12-16
2 2013-01-29 2013-01-29
1 2013-01-16 2013-01-16
2 2013-02-28 2013-02-28
create a number list on the fly
然后,您需要将此连接到主表,并执行计数:
SELECT u.ID,
u.PeriodStart,
DATE_ADD(PeriodEnd, INTERVAL -1 DAY) PeriodEnd,
COUNT(*) AS `COUNT`
FROM ( SELECT u.ID,
DATE_ADD(RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(RegisteredDate, INTERVAL n.Number + 1 MONTH) PeriodEnd
FROM User u
CROSS JOIN Numbers n
) u
INNER JOIN T
ON T.ID = u.ID
AND T.Date >= u.PeriodStart
AND T.Date < PeriodEnd
GROUP BY u.ID, u.PeriodStart, u.PeriodEnd;
给出最终结果:
ID PERIODSTART PERIODEND COUNT
1 2012-12-16 2013-01-15 1
1 2013-01-16 2013-02-15 3
2 2013-02-28 2013-03-28 3
2 2013-03-29 2013-04-28 2
<强> Example on SQL Fiddle 强>
显然,您可以连接句点的开始日期和结束日期以制作“范围”字符串,但这可能最适合在您的应用程序层中处理。
修改强>
这可以通过没有可能表现更好的子查询来实现:
SELECT u.ID,
DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH), INTERVAL -1 DAY) PeriodEnd,
COUNT(*) AS `COUNT`
FROM User u
CROSS JOIN Numbers n
INNER JOIN T
ON T.ID = u.ID
AND T.Date >= DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH)
AND T.Date < DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH)
GROUP BY u.ID, u.RegisteredDate, n.Number;
<强> Full Example on SQL-Fiddle 强>
编辑2
这将为您提供所有用户的所有期间,直至当前期间(即今天属于该日期范围内)
SELECT u.ID,
DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH), INTERVAL -1 DAY) PeriodEnd,
COUNT(T.ID) AS `COUNT`
FROM User u
CROSS JOIN Numbers n
LEFT JOIN T
ON T.ID = u.ID
AND T.Date >= DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH)
AND T.Date < DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH)
WHERE DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH) <= CURRENT_TIMESTAMP
GROUP BY u.ID, u.RegisteredDate, n.Number;