MySQL中按周、月、季、年分组统计

本文详细介绍了如何利用内建日期处理函数和MySQL查询,对金融数据按年、季、月、周进行分组统计,包括NOW(), UNIX_TIMESTAMP(), DATE_FORMAT()等函数的用法,并提供了SQL示例。适合初学者理解日期操作在数据库中的应用。

根据日期,按照周、月、季、年对数据进行分组统计
一、前言
    带着问题去学习,我觉得是一种非常有效的学习方法,不知下面的几个问题是否也是你所需要考虑的:被分组的日期是否连续、周是以周日为起始日,还是以周一为起始日、日期跨年。在讨论日期查询的复杂性之前,我们有必要知道一些内建日期处理函数和Date/Time(日期/时间)数据类型,已经有一定了解的快速浏览下就好,直接去后半部分享用正餐。

二、内建日期处理函数
NOW() 函数
  定义和用法:

NOW() 返回当前的日期和时间。
 查询

select now();
结果

UNIX_TIMESTAMP() 函数
定义和用法:

1  无参数调用:UNIX_TIMESTAMP()  返回值:自'1970-01-01 00:00:00'的到当前时间的秒数差
2  有参数调用:UNIX_TIMESTAMP(date) 返回值:自'1970-01-01 00:00:00'与指定时间的秒数差
查询:

select UNIX_TIMESTAMP(), UNIX_TIMESTAMP('20180912');
 结果: 

DATE_FORMAT() 函数 
定义和用法:

DATE_FORMAT(date,format) 函数用于以不同的格式显示日期/时间数据。
查询:

select DATE_FORMAT(NOW(),'%Y-%m-%d');
结果: 

三、 Date类型
date数据类型
数据类型    描述
DATE()    日期。格式:YYYY-MM-DD
注释:支持的范围是从 '1000-01-01' 到 '9999-12-31'

DATETIME()    *日期和时间的组合。格式:YYYY-MM-DD HH:MM:SS
注释:支持的范围是从 '1000-01-01 00:00:00' 到 '9999-12-31 23:59:59'

TIMESTAMP()    *时间戳。TIMESTAMP 值使用 Unix 纪元('1970-01-01 00:00:00' UTC) 至今的秒数来存储。格式:YYYY-MM-DD HH:MM:SS
注释:支持的范围是从 '1970-01-01 00:00:01' UTC 到 '2038-01-09 03:14:07' UTC

TIME()    时间。格式:HH:MM:SS
注释:支持的范围是从 '-838:59:59' 到 '838:59:59'

YEAR()    2 位或 4 位格式的年。
注释:4 位格式所允许的值:1901 到 2155。2 位格式所允许的值:70 到 69,表示从 1970 到 2069。

注: 即便 DATETIME 和 TIMESTAMP 返回相同的格式,它们的工作方式很不同。
在 INSERT 或 UPDATE 查询中,TIMESTAMP 自动把自身设置为当前的日期和时间。
TIMESTAMP 也接受不同的格式,比如 YYYYMMDDHHMMSS、YYMMDDHHMMSS、YYYYMMDD 或 YYMMDD。
format 可以使用的格式有:
格式    描述
%a    缩写星期名
%b    缩写月名
%c    月,数值
%D    带有英文前缀的月中的天
%d    月的天,数值(00-31)
%e    月的天,数值(0-31)
%f    微秒
%H    小时(00-23)
%h    小时(01-12)
%I    小时(01-12)
%i    分钟,数值(00-59)
%j    年的天(001-366)
%k    小时(0-23)
%l    小时(1-12)
%M    月名
%m    月,数值(00-12)
%p    AM 或 PM
%r    时间,12-小时(hh:mm:ss AM 或 PM)
%S    秒(00-59)
%s    秒(00-59)
%T    时间, 24-小时(hh:mm:ss)
%U    周(00-53)星期日是一周的第一天
%u    周(00-53)星期一是一周的第一天
%V    周(01-53)星期日是一周的第一天,与 %X 使用
%v    周(01-53)星期一是一周的第一天,与 %x 使用
%W    星期名
%w    周的天(0=星期日, 6=星期六)
%X    年,其中的星期日是周的第一天,4 位,与 %V 使用
%x    年,其中的星期一是周的第一天,4 位,与 %v 使用
%Y    年,4 位
%y    年,2 位
 

四、分组统计
说明
我们先在数据库中创建一张表

SET FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for date_group
-- ----------------------------
DROP TABLE IF EXISTS `date_group`;
CREATE TABLE `date_group` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `ei` bigint(20) NOT NULL,
  `day` varchar(25) DEFAULT NULL COMMENT '日期',
  `openPrice` decimal(25,2) DEFAULT NULL COMMENT '开盘价',
  `closePrice` decimal(25,2) DEFAULT NULL COMMENT '收盘价',
  `lowPrice` decimal(25,2) DEFAULT NULL COMMENT '最低价',
  `higPrice` decimal(25,2) DEFAULT NULL COMMENT '最高价',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Records of date_group
-- ----------------------------
INSERT INTO `date_group` VALUES ('1', '10001', '20161230', '10.00', '20.00', '5.00', '25.00');
INSERT INTO `date_group` VALUES ('2', '10001', '20161231', '11.00', '19.00', '6.00', '26.00');
INSERT INTO `date_group` VALUES ('3', '10001', '20170101', '12.00', '18.00', '7.00', '27.00');
INSERT INTO `date_group` VALUES ('5', '10001', '20170102', '12.00', '18.00', '7.00', '27.00');
INSERT INTO `date_group` VALUES ('6', '10001', '20170203', '13.00', '19.00', '8.00', '21.00');
INSERT INTO `date_group` VALUES ('7', '10001', '20170204', '15.00', '20.00', '9.00', '24.00');
INSERT INTO `date_group` VALUES ('8', '10001', '20170205', '13.00', '12.00', '4.00', '27.00');
INSERT INTO `date_group` VALUES ('9', '10001', '20170206', '12.00', '13.00', '5.00', '25.00');
INSERT INTO `date_group` VALUES ('10', '10001', '20180109', '14.00', '11.00', '6.00', '22.00');
INSERT INTO `date_group` VALUES ('11', '10001', '20180110', '17.00', '18.00', '8.00', '23.00');
INSERT INTO `date_group` VALUES ('12', '10001', '20180111', '19.00', '12.00', '9.00', '22.00');
INSERT INTO `date_group` VALUES ('13', '10001', '20180112', '13.00', '16.00', '2.00', '25.00');
INSERT INTO `date_group` VALUES ('14', '10001', '20180113', '11.00', '17.00', '1.00', '28.00');
INSERT INTO `date_group` VALUES ('15', '10001', '20180114', '10.00', '13.00', '8.00', '24.00');
INSERT INTO `date_group` VALUES ('16', '10002', '20161230', '15.00', '22.00', '5.00', '25.00');
INSERT INTO `date_group` VALUES ('17', '10002', '20161231', '13.00', '19.00', '6.00', '22.00');
INSERT INTO `date_group` VALUES ('18', '10002', '20170101', '12.00', '14.00', '7.00', '23.00');
INSERT INTO `date_group` VALUES ('19', '10002', '20170102', '14.00', '18.00', '7.00', '22.00');
INSERT INTO `date_group` VALUES ('20', '10002', '20170203', '17.00', '12.00', '8.00', '23.00');
INSERT INTO `date_group` VALUES ('21', '10002', '20170204', '19.00', '20.00', '9.00', '22.00');
INSERT INTO `date_group` VALUES ('22', '10002', '20170205', '13.00', '17.00', '4.00', '22.00');
INSERT INTO `date_group` VALUES ('23', '10002', '20170206', '11.00', '13.00', '6.00', '25.00');
INSERT INTO `date_group` VALUES ('24', '10002', '20180109', '10.00', '17.00', '8.00', '28.00');
INSERT INTO `date_group` VALUES ('25', '10002', '20180110', '17.00', '18.00', '7.00', '24.00');
INSERT INTO `date_group` VALUES ('26', '10002', '20180111', '19.00', '25.00', '8.00', '22.00');
INSERT INTO `date_group` VALUES ('27', '10002', '20180112', '13.00', '19.00', '9.00', '25.00');
INSERT INTO `date_group` VALUES ('28', '10002', '20180113', '15.00', '26.00', '4.00', '28.00');
INSERT INTO `date_group` VALUES ('29', '10002', '20180114', '13.00', '16.00', '5.00', '24.00');
 

按年分组统计
-- 第一种
SELECT a.ei, a.day, DATE_FORMAT(a.day,'%Y') year, SUM(a.openPrice) FROM date_group AS a GROUP BY a.ei, year;
-- 第二种
SELECT a.ei, a.day, YEAR(a.day) year, AVG(a.closePrice) FROM date_group AS a GROUP BY a.ei, year;
 

按季分组统计 
-- 注意区分出 年
 
-- 第一种
SELECT a.ei, a.day, CONCAT(YEAR(a.day),FLOOR((date_format(a.day, '%m')+2)/3)) quarter, SUM(a.openPrice) FROM date_group AS a GROUP BY a.ei, quarter;
-- 第二种
SELECT a.ei, a.day, CONCAT(YEAR(a.day),QUARTER(a.day)) quarter, AVG(a.closePrice) FROM date_group AS a GROUP BY a.ei, quarter;
按月分组统计
-- 第一种
SELECT a.ei, a.day, date_format(a.day, '%Y%m') month, SUM(a.openPrice) FROM date_group AS a GROUP BY a.ei, month;
 
-- 第二种
-- month(date)函数 和 %c 差不多,返回的是月的值(如一月为 ‘1’)
SELECT a.ei, a.day, CONCAT(YEAR(a.day),MONTH(a.day)) month, AVG(a.closePrice) FROM date_group AS a GROUP BY a.ei, month;
SELECT a.ei, a.day, date_format(a.day, '%Y%c') month, AVG(a.closePrice) FROM date_group AS a GROUP BY a.ei, month;
按周分组统计
-- 第一种
-- 以周一为一周起始,所以20170101周日,为2016年最后一周,20170102 为2017年第一周
SELECT a.ei, a.day, date_format(a.day, '%x%v') week, SUM(a.lowPrice) FROM date_group AS a GROUP BY a.ei, week;
SELECT a.ei, a.day, (UNIX_TIMESTAMP(a.day) - (if(date_format(a.day, '%w'), date_format(a.day, '%w') - 1, 6) * 86400) - 316800) / 604800 week, SUM(a.lowPrice) FROM date_group AS a GROUP BY a.ei, week;
 
 
-- 第二种
-- 以周日为一周起始,所以20170101位2017年第一周
SELECT a.ei, a.day, date_format(a.day, '%X%V') week, SUM(a.lowPrice) FROM date_group AS a GROUP BY a.ei, week;
SELECT a.ei, a.day, (UNIX_TIMESTAMP(a.day) - date_format(a.day, '%w') * 86400 - 316800) / 604800 week, SUM(a.lowPrice) FROM date_group AS a GROUP BY a.ei, week;
五、后记
    这是一篇来源于最近业务需求的总结,简化版了很多。之所以挑选这个需求,这是我第一次想到写博客时定下的日子,所以在平时有注意别的博主怎么写的,但真的当自己写起来时发现不是那么容易,有些代码里的想法不知道要不要表达出来,怕过于啰嗦,如果这篇文章有幸能被有经验的博主阅读到,希望能给我点指导,同时也期待其它读者关于就这篇文章的不足之处讨论交流。
 

<think>我们有一个给定日期'2025-02-15',需要统计该日期所在度(即第一度)的每个的数据。在MySQL中,我们可以通过以下步骤实现:1.**确定度范围**:根据给定日期计算出度的开始份和结束份。这里2025-02-15属于第一度(1、2、3)。2.**按分组**:从数据表中筛选出该度内的所有数据,然后使用MySQL的日期函数提取份,并按分组。3.**统计每个份的数据**:对每个分组进行聚合统计(如计数、求和等)。###实现步骤####步骤1:计算度范围我们可以使用MySQL的日期函数来计算给定日期所在的度。MySQL中可以使用`QUARTER()`函数获取度,但为了获取每个分组,我们需要将数据按份提取。####步骤2:构建查询语句假设我们有一个表`sales`,其中有一个日期字段`sale_date`和一个数值字段`amount`,我们需要统计2025第一度(1、2、3)每个的总销售额。查询语句如下:```sqlSELECTMONTH(sale_date)ASmonth,--提取份SUM(amount)AStotal_amount,--按求和COUNT(*)AStotal_records--按计数FROMsalesWHEREsale_date>='2025-01-01'ANDsale_date<'2025-04-01'--注意:这里使用<'2025-04-01',因为日期范围是[2025-01-01,2025-03-31]GROUPBYMONTH(sale_date)ORDERBYmonth;```####步骤3:动态确定度范围如果我们需要根据给定日期动态计算度范围,可以使用以下方法:```sqlSET@given_date='2025-02-15';--计算度的起始份和结束份SELECTMONTH(sale_date)ASmonth,SUM(amount)AStotal_amount,COUNT(*)AStotal_recordsFROMsalesWHEREsale_date>=MAKEDATE(YEAR(@given_date),1)+INTERVAL(QUARTER(@given_date)-1)*3-1MONTHANDsale_date<MAKEDATE(YEAR(@given_date),1)+INTERVALQUARTER(@given_date)*3-1MONTHGROUPBYMONTH(sale_date);```但是,上述方法中日期计算比较复杂,我们可以简化:```sqlSET@given_date='2025-02-15';--计算度的起始日期和结束日期SET@quarter_start=CONCAT(YEAR(@given_date),'-',(QUARTER(@given_date)*3-2),'-01');SET@quarter_end=DATE_ADD(DATE_ADD(@quarter_start,INTERVAL3MONTH),INTERVAL-1DAY);--然后使用这两个变量进行过滤SELECTMONTH(sale_date)ASmonth,SUM(amount)AStotal_amount,COUNT(*)AStotal_recordsFROMsalesWHEREsale_dateBETWEEN@quarter_startAND@quarter_endGROUPBYMONTH(sale_date);```然而,注意:使用`BETWEEN`是包含边界值的,所以如果我们的日期字段包含时间部分,那么最好使用:```sqlWHEREsale_date>=@quarter_startANDsale_date<DATE_ADD(@quarter_start,INTERVAL3MONTH)```这样能确保包含整个度(从度的第一天开始,到下度的第一天之前)。###完整示例```sql--设置给定日期SET@given_date='2025-02-15';--计算度开始日期SET@quarter_start=MAKEDATE(YEAR(@given_date),1)+INTERVAL(QUARTER(@given_date)-1)*3MONTH;--计算度结束日期(下度的开始日期)SET@next_quarter_start=MAKEDATE(YEAR(@given_date),1)+INTERVALQUARTER(@given_date)*3MONTH;--查询SELECTMONTH(sale_date)ASmonth,SUM(amount)AStotal_amount,COUNT(*)AStotal_recordsFROMsalesWHEREsale_date>=@quarter_startANDsale_date<@next_quarter_startGROUPBYMONTH(sale_date)ORDERBYmonth;```###解释1.`MAKEDATE(YEAR(@given_date),1)`:生成给定份的第一天(即'2025-01-01')。2.`(QUARTER(@given_date)-1)*3MONTH`:计算从初到度开始需要增加的份数。例如,第一度增加0个,第二度增加3个,以此类推。3.因此,`@quarter_start`就是度的第一天,`@next_quarter_start`是下一度的第一天。4.然后我们筛选出在`[@quarter_start,@next_quarter_start)`区间内的数据。###注意事项-如果日期字段包含时间部分,使用`>=`和`<`可以避免最后一天的时间部分问题。-如果数据量很大,确保在`sale_date`上有索引。###引用说明在MySQL中,我们可以使用`QUARTER()`函数来获取度[^1]。同时,我们使用了日期计算(如`MAKEDATE`和`INTERVAL`)来构建度的起始和结束日期,这类似于引用[1]中提到的日期计算[^1]。另外,我们使用了`MONTH()`函数来提取份,这与引用[2]中提取份的方法类似[^2]。###相关问题1.如何在MySQL统计度每个度的数据?2.如何按分组统计数据?3.在MySQL中,如何计算两个日期之间的工作日数?</think># MySQL 度每数据分组统计方案 根据您的需求,我将介绍如何使用MySQL对给定日期(2025-02-15)所在度进行每数据分组统计。 ## 解决方案思路 1. **确定度范围**:计算给定日期所属度的份范围 2. **按分组**:使用日期函数提取份并分组 3. **统计指标**:对每个份的数据进行聚合统计 4. **动态日期处理**:使查询适用于任意日期 ## 完整SQL实现 ```sql -- 设置变量便于复用 SET @given_date = '2025-02-15'; -- 计算度起始份 SET @quarter_start_month = QUARTER(@given_date) * 3 - 2; SET @year = YEAR(@given_date); -- 按分组统计查询 SELECT MONTH(your_date_column) AS month, DATE_FORMAT(your_date_column, '%M') AS month_name, COUNT(*) AS record_count, SUM(your_value_column) AS total_value, AVG(your_value_column) AS avg_value, MAX(your_value_column) AS max_value, MIN(your_value_column) AS min_value FROM your_table WHERE YEAR(your_date_column) = @year AND QUARTER(your_date_column) = QUARTER(@given_date) GROUP BY month, month_name ORDER BY month; ``` ## 查询结果示例 假设有销售数据表`sales_data`: | sale_date | amount | |-------------|--------| | 2025-01-05 | 150.00 | | 2025-01-15 | 200.00 | | 2025-02-10 | 100.00 | | 2025-02-20 | 250.00 | | 2025-03-05 | 300.00 | | 2025-03-25 | 180.00 | 查询结果将显示: | month | month_name | record_count | total_value | avg_value | max_value | min_value | |-------|------------|--------------|-------------|-----------|-----------|-----------| | 1 | January | 2 | 350.00 | 175.00 | 200.00 | 150.00 | | 2 | February | 2 | 350.00 | 175.00 | 250.00 | 100.00 | | 3 | March | 2 | 480.00 | 240.00 | 300.00 | 180.00 | ## 关键函数说明 1. **`QUARTER(date)`**:返回日期所在的度(1-4) ```sql SELECT QUARTER('2025-02-15'); -- 返回1(第一度) ``` 2. **`MONTH(date)`**:提取日期的份(1-12) ```sql SELECT MONTH('2025-02-15'); -- 返回2 ``` 3. **`DATE_FORMAT(date, format)`**:格式化日期显示 ```sql SELECT DATE_FORMAT('2025-02-15', '%M'); -- 返回'February' ``` 4. **聚合函数**: - `COUNT()`:统计记录数 - `SUM()`:计算总和 - `AVG()`:计算平均值 - `MAX()`/`MIN()`:获取最大/最小值 ## 优化建议 ### 1. 添加日期范围索引 ```sql ALTER TABLE your_table ADD INDEX idx_date (your_date_column); ``` ### 2. 使用更精确的日期范围过滤 ```sql WHERE your_date_column >= MAKEDATE(@year, 1) + INTERVAL (@quarter_start_month - 1) MONTH AND your_date_column < MAKEDATE(@year, 1) + INTERVAL (@quarter_start_month + 2) MONTH ``` ### 3. 处理包含时间部分的日期 ```sql WHERE your_date_column >= CONCAT(@year, '-', LPAD(@quarter_start_month, 2, '0'), '-01') AND your_date_column < CONCAT(@year, '-', LPAD(@quarter_start_month + 3, 2, '0'), '-01') ``` ## 动态日期处理 要使查询适用于任意日期,只需修改变量值: ```sql SET @given_date = '2025-05-20'; -- 自动计算第二度 ``` ## 相关问题 1. 如何在MySQL统计度每数据? [参考方案:使用`YEAR(date)`和`MONTH(date)`结合,按半分组](#) 2. 如何将统计结果导出为CSV文件? [参考方案:使用`SELECT ... INTO OUTFILE`语句](#) 3. 在MySQL中如何实现跨统计? [参考方案:使用`YEAR(date)`和`QUARTER(date)`分组](#) 4. 如何优化大型数据集的统计性能? [参考方案:添加日期索引、分区表、使用物化视图](#) 5. 如何在MySQL中计算度同比增长率? [参考方案:使用窗口函数或自连接比较不同份的度数据](#) 以上解决方案充分利用了MySQL的日期函数和聚合功能,可以高效地实现度内每数据的分组统计
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值