如何在 MySQL 中按周分组?

Posted

技术标签:

【中文标题】如何在 MySQL 中按周分组?【英文标题】:How to group by week in MySQL? 【发布时间】:2010-12-16 16:47:39 【问题描述】:

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') TRUNC(date, 'DY' ) 获得星期天开始的几周。 'W' 周一开始,至少在我的系统中。 【参考方案1】:

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

不过分优雅,但实用...

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

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

编辑:作为 Martin points out,您也可以使用 YEARWEEK(mysqldatefield) 函数,尽管它的输出不如上面较长的公式那么眼睛友好。


编辑 2 [3 1/2 年后!]:YEARWEEK(mysqldatefield) 将可选的第二个参数 (mode) 设置为 0 或 2 可能是最好的方法按完整周汇总(即包括跨越 1 月 1 日的周数),如果需要的话。此答案中最初提出的YEAR() / WEEK() 方法具有将此类“跨界” 周的聚合数据分成两部分的效果:一个与前一年,一个与新年。 在会计等方面通常需要以最多两个部分周为代价的每年一次的干净利落,因此YEAR() / WEEK() 方法更好。

【讨论】:

YEARWEEK() 会产生一个INT(6),我相信使用它进行排序/加入/分组会更快 @mjv:如果两年时间相同,会发生什么?喜欢这个 31-12-2012 到 6-1-2013(周一到周日)。有什么解决办法吗? @hardik:参见编辑 2。根据个人的需要,YEARWEEK() 可能是一个更好的聚合键,如果一个人宁愿使用完整的周而不是拆分最后/第一部分周年。 @mjv 这是另一个解决方案link 要显示日期值,您可以将其转换回一周的第一天:select str_to_date(concat(yearweek('2019-01-01',2),'0'), '%X%V%w');【参考方案2】:

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

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

而且,如果您的业务规则规定您的周从星期一开始,请将 -1 更改为 -2


编辑

几年过去了,我终于有时间写这篇文章了。 https://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

【讨论】:

@Ollie - 我会考虑使用 DAYOFWEEK() 或 WEEKDAY() 来提高可读性。 Martin,我考虑了这一点,发现这些函数在周一至周日运行 0-6。我的业务规则说星期从 00:00 开始,所以 WEEKDAY 的内容有点难看。您对可读性的看法是正确的。 @OllieJones 完美!非常感谢! 很棒的解决方案和文章。谢谢! 有没有办法将“星期一”设置为第一天?【参考方案3】:

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

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

【讨论】:

按日期排序 (ORDER BY date ASC) 也可以解决问题 @Fender 如果表设置为仅允许严格的分组列,您将无法将“日期”作为 ORDER 进行分组(日期不在分组按年和周版本中) , 现在是 MYSQL 的默认设置【参考方案4】:

您可以使用YEARWEEK() 函数获取连接的年份和周数 (200945)。如果我正确理解您的目标,那应该可以让您对多年数据进行分组。

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

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

对于按月排序,您可能会考虑使用LAST_DAY() 函数 - 将按月的最后一天排序,但这应该等同于按月的第一天排序......不是吗?

【讨论】:

+1 马丁。对我来说,我忘记了 YEARWEEK()... 我只是不怎么用它。【参考方案5】:

只需在选择中添加此广告:

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

还有

group_by(week);

【讨论】:

如何按月分组 它不会连续抓取数据,而是只抓取一个特定日期【参考方案6】:

如果您需要“周结束”日期,这也可以。这将计算每周的记录数。示例:如果在 2010 年 1 月 2 日(含)和 2010 年 1 月 8 日之间创建了三个工作订单,并且在 2010 年 1 月 9 日和 2010 年 1 月 16 日(含)之间创建了 5 个,则返回:

2010 年 1 月 8 日 3 日 5 2010 年 1 月 16 日

我不得不使用额外的 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;

【讨论】:

【参考方案7】:

上周日:

STR_TO_DATE(CONCAT(YEARWEEK(timestamp,2),'0'),'%X%V%w')

上周一:

STR_TO_DATE(CONCAT(YEARWEEK(timestamp,3),'1'),'%x%v%w')

DATE_FORMAT(date,format) reference:

%V - 周 (01..53),其中星期日是一周的第一天; WEEK() 模式 2;与%X 一起使用 %v - 周 (01..53),其中星期一是一周的第一天; WEEK() 模式 3;与%x 一起使用 %w - 星期几(0=星期日..6=星期六) %X - 星期天是一周的第一天,数字,四位数字;与%V 一起使用 %x - 一周的年份,其中星期一是一周的第一天,数字,四位数字;与%v 一起使用

【讨论】:

【参考方案8】:

我喜欢 MySQL 中的 week 函数,但在我的情况下,我想知道 一个月中的哪一周。我使用了这个解决方案:

其中run_date 是像2021-02-25 00:00:00 这样的时间戳

concat ( 
        date_format(run_date, '%Y-%m'), 
        ' wk ', 
        (week(run_date,1) - ( week(date_format(run_date, '%Y-%m-01')) - 1))
        ) as formatted_date

这个输出:

2021-02-23 --->    2021-02 wk 4
2021-02-25 --->    2021-02 wk 4
2021-02-11 --->    2021-02 wk 2
2021-03-02 --->    2021-03 wk 1

这背后的想法是,我想(相对确定地)知道该日期是在所讨论的月份的哪一周发生的?

所以我们连接:

date_format(run_date, '%Y-%m') 获取2021-02

然后我们添加文字字符串wk

然后我们使用: week(run_date, 1) 获取该记录的周(1 开始星期一),(这将是 7 因为 2021 年 2 月 21 日是一年中的第 7 周,我们减去该周的任何内容同一个月的第一天 - 2021-02-01week() 是 5,因为它是在一年中的第 5 周:

(week(date_format(run_date, '%Y-%m-01'))

不幸的是,这将从 0 开始计数,这是人们不喜欢的,所以我们从连接结果的最后部分减去 1,以便“周”从 1 开始。

【讨论】:

【参考方案9】:

这可能是一个不错的选择:

SELECT
 year(datetime_field) as year_date, week(datetime_field) as week_date
FROM
 bd.table
GROUP BY
 year_date, week_date;

看起来像这样:

'2020', '14' '2020', '15' '2020', '16' '2020','17' '2020', '18'

【讨论】:

在使用前用跨越一年到另一年的数据进行测试。 我测试过了,谢谢告知。

以上是关于如何在 MySQL 中按周分组?的主要内容,如果未能解决你的问题,请参考以下文章

如何获得最近 x 周数据的不同计数,但在红移中按周分组?

在熊猫中按周分组

如何使用 Django ORM 按周分组

如何在 iOS 中按周/月/每 X 天添加开始日期和结束日期的余数?

如何在 MySQL Select 语句中按多列分组

在mysql中计算数据并按周分组