如何在MySQL中按周分组?


91

Oracle的表服务器提供了一个内置功能TRUNC(timestamp,'DY')。此函数将任何时间戳转换为前一个星期日的午夜。在MySQL中执行此操作的最佳方法是什么?

Oracle还提供TRUNC(timestamp,'MM')了将时间戳转换为发生月份的第一天的午夜。在MySQL中,这很简单:

TIMESTAMP(DATE_FORMAT(timestamp, '%Y-%m-01'))

但是这个 DATE_FORMAT技巧将在数周内失效。我知道此WEEK(timestamp)功能,但是我真的不想要一年内的星期数。这些东西是用于多年的工作。


您的意思是trunc(sysdate,'W'),而不是trunc(sysdate,'DY')
David Aldridge

TRUNC(date, 'DY' )周日开始需要几个星期。'W'至少在我的系统中,周一开始。
O. Jones

Answers:


123

您可以同时使用YEAR(timestamp)WEEK(timestamp),并在SELECTGROUP BY子句中使用这两个表达式。

不太优雅,但功能...

当然,您也可以将这两个日期部分组合为一个表达式,例如

SELECT CONCAT(YEAR(timestamp), '/', WEEK(timestamp)), etc...
FROM ...
WHERE ..
GROUP BY CONCAT(YEAR(timestamp), '/', WEEK(timestamp))

编辑:正如马丁指出,您还可以使用YEARWEEK(mysqldatefield)函数,尽管其输出不如上面的较长公式对眼睛友好。


编辑2 [3年半后!]:
YEARWEEK(mysqldatefield)使用可选的第二个参数(mode设置为0或2)可能是通过聚合的最佳途径完整周(即包括了几个星期,其跨在1月1日),如果是所需要的。YEAR() / WEEK()在此答案中最初提出的方法具有将这种“跨”周的汇总数据一分为二的效果:一个与前一年,一个与新年。
在会计等方面,通常需要每年进行一次完整的清理,以不超过两个半周为代价,在任一末端都需要一个星期,为此,这种YEAR() / WEEK()方法更好。


12
YEARWEEK()导致INT(6)我相信与
KCD 2012年

@mjv:两年共享同一周会怎样?例如,2012年12月31日至2013年6月1日(周一至周日)。有什么解决办法吗?
hardik

@hardik:请参阅编辑2。如果希望将YEARWEEK()与整周工作而不是将一年的最后/第一周分开工作,则可以根据自己的需要选择YEARWEEK()。
mjv

@mjv这是该链接
hardik 2013年

确保“时间戳记”不是整数,就像我在表中那样。WEEK(timestamp)将日期/时间戳记列类型作为有效参数
Usman Shaukat 2015年

37

上面接受的答案对我不起作用,因为它按字母顺序而不是按时间顺序对星期进行排序:

2012/1
2012/10
2012/11
...
2012/19
2012/2

这是我按周计数和分组的解决方案:

SELECT CONCAT(YEAR(date), '/', WEEK(date)) AS week_name, 
       YEAR(date), WEEK(date), COUNT(*)
FROM column_name
GROUP BY week_name
ORDER BY YEAR(DATE) ASC, WEEK(date) ASC

产生:

YEAR/WEEK   YEAR   WEEK   COUNT
2011/51     2011    51      15
2011/52     2011    52      14
2012/1      2012    1       20
2012/2      2012    2       14
2012/3      2012    3       19
2012/4      2012    4       19

4
按日期(ORDER BY date ASC)排序也可以解决问题
芬达(Fender)

36

想通了...有点麻烦,但这就是。

FROM_DAYS(TO_DAYS(TIMESTAMP) -MOD(TO_DAYS(TIMESTAMP) -1, 7))

并且,如果您的业务规则说您的工作周从星期一开始,请将更改-1-2


编辑

几年过去了,我终于可以写这篇文章了。 http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/


@Ollie-我会考虑使用DAYOFWEEK()或WEEKDAY()来提高可读性。
马丁·克莱顿

1
马丁,我考虑了一下,发现这些函数在周一至周日运行0-6。我的业务规则说,星期几从星期日的00:00开始,因此WEEKDAY的内容有点难看。您的可读性是正确的。
O. Jones

@OllieJones完美!非常感谢!
Joe Cheng

1
很棒的解决方案和文章。谢谢!
杰里米·威金斯

有没有办法将“星期一”设置为第一天?
PedroJoaquín19年

25

您可以使用YEARWEEK()获得级联的年份和星期数(200945 函数。如果我正确地理解了您的目标,则应该可以对多年数据进行分组。

如果您需要本周开始时的实际时间戳记,那就不太好了:

DATE_SUB( field, INTERVAL DAYOFWEEK( field ) - 1 DAY )

对于每月订购,您可以考虑使用LAST_DAY()函数-排序是按当月的最后一天进行的,但这应该等效于按月的第一天进行排序……不是吗?


1
+1马丁。对我而言,我忘了YEARWEEK()...我只是很少使用它。
mjv

4

只需在选择广告:

DATE_FORMAT($yourDate, \'%X %V\') as week

group_by(week);

如何按月分组
Ravi Teja

它不会连续获取数据,而只能获取一个特定日期
Chique_Code

2

如果您需要“星期结束”日期,那么它也将起作用。这将计算每周的记录数。示例:如果在(含)1/2/2010和1/8/2010之间创建了三个工单,而在(含)1/9/2010和1/16/2010之间创建了五个工单,则返回:

3 1/8/2010
5 1/16/2010

我必须使用额外的DATE()函数来截断日期时间字段。

SELECT COUNT(*), DATE_ADD( DATE(wo.date_created), INTERVAL (7 - DAYOFWEEK( wo.date_created )) DAY) week_ending
FROM work_order wo
GROUP BY week_ending;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.