各位观众老爷们,大家好!我是你们的老朋友,今天咱们来聊聊MySQL里那些让人又爱又恨的日期和时间函数。别害怕,它们其实没那么难搞,掌握了它们,你就能像个时间旅行者一样,在数据库里玩转各种日期,格式化成你想要的任何样子!
开场白:时间都去哪儿了?(以及MySQL怎么帮你找回来)
咱们先来个小剧场:
你(抓狂):老板,我要查上个月的销售额!
MySQL(淡定):SELECT SUM(amount) FROM sales WHERE sale_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 1 MONTH) AND CURDATE();
你(一脸懵):这是啥?
MySQL(微笑):这就是时间魔法!
所以,今天咱们就来学习这些时间魔法,让你也能像MySQL一样,轻松应对各种日期时间的需求。
第一幕:时间的基石 – 基本的日期时间类型
在开始魔法之前,咱们得先认识一下舞台上的演员 – MySQL的日期时间类型。
| 数据类型 | 描述 | 格式 | 范围 |
|---|---|---|---|
| DATE | 日期,只包含年、月、日 | YYYY-MM-DD |
1000-01-01 到 9999-12-31 |
| TIME | 时间,只包含时、分、秒 | HH:MM:SS |
-838:59:59 到 838:59:59 |
| DATETIME | 日期和时间的组合 | YYYY-MM-DD HH:MM:SS |
1000-01-01 00:00:00 到 9999-12-31 23:59:59 |
| TIMESTAMP | 时间戳,存储自1970-01-01 00:00:00 UTC以来的秒数,与时区相关。 | YYYY-MM-DD HH:MM:SS |
1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC (32位系统限制) |
| YEAR | 年份,可以是2位或4位数字 | YYYY 或 YY |
1901 到 2155 (4位), 70 到 69 (2位,转换为1970-2069) |
注意事项:
TIMESTAMP会根据服务器的时区自动转换,而DATETIME不会。TIMESTAMP的范围有限,要注意别超出范围。YEAR(2)已经过时,不建议使用。
第二幕:时间的魔法棒 – 常用的日期时间函数
有了演员,咱们就得给他们配上魔法棒 – MySQL的日期时间函数。 这些函数能让你轻松获取当前时间、提取日期部分、进行日期计算等等。
1. 获取当前日期时间
-
CURDATE()或CURRENT_DATE(): 获取当前日期 (YYYY-MM-DD)SELECT CURDATE(); -- 输出:2023-10-27 (假设今天是2023年10月27日) -
CURTIME()或CURRENT_TIME(): 获取当前时间 (HH:MM:SS)SELECT CURTIME(); -- 输出:14:30:00 (假设现在是下午2点30分) -
NOW()或SYSDATE()或CURRENT_TIMESTAMP(): 获取当前日期和时间 (YYYY-MM-DD HH:MM:SS)SELECT NOW(); -- 输出:2023-10-27 14:30:00 (假设今天是2023年10月27日下午2点30分)
2. 提取日期时间部分
-
YEAR(date): 提取年份SELECT YEAR('2023-10-27'); -- 输出:2023 -
MONTH(date): 提取月份SELECT MONTH('2023-10-27'); -- 输出:10 -
DAY(date)或DAYOFMONTH(date): 提取日SELECT DAY('2023-10-27'); -- 输出:27 -
HOUR(time): 提取小时SELECT HOUR('14:30:00'); -- 输出:14 -
MINUTE(time): 提取分钟SELECT MINUTE('14:30:00'); -- 输出:30 -
SECOND(time): 提取秒SELECT SECOND('14:30:00'); -- 输出:0 -
DAYNAME(date): 提取星期几 (英文)SELECT DAYNAME('2023-10-27'); -- 输出:Friday -
MONTHNAME(date): 提取月份名称 (英文)SELECT MONTHNAME('2023-10-27'); -- 输出:October -
DAYOFWEEK(date): 返回星期几 (1 = 星期日, 2 = 星期一, …, 7 = 星期六)SELECT DAYOFWEEK('2023-10-27'); -- 输出:6 (星期五) -
DAYOFYEAR(date): 返回一年中的第几天SELECT DAYOFYEAR('2023-10-27'); -- 输出:300 -
WEEK(date): 返回一年中的第几周SELECT WEEK('2023-10-27'); -- 输出:43
3. 日期时间计算
-
DATE_ADD(date, INTERVAL expr unit)或DATE_SUB(date, INTERVAL expr unit): 日期加减date: 要进行计算的日期INTERVAL expr unit: 时间间隔,expr是数值,unit是时间单位,可以是YEAR,MONTH,DAY,HOUR,MINUTE,SECOND等。
-- 加一天 SELECT DATE_ADD('2023-10-27', INTERVAL 1 DAY); -- 输出:2023-10-28 -- 减一个月 SELECT DATE_SUB('2023-10-27', INTERVAL 1 MONTH); -- 输出:2023-09-27 -- 加一年两个月三天 SELECT DATE_ADD('2023-10-27', INTERVAL '1 2' YEAR_MONTH); -- 输出:2024-12-27 SELECT DATE_ADD('2023-10-27', INTERVAL '3' DAY); -- 输出:2023-10-30 -
ADDDATE(date, INTERVAL expr unit)和SUBDATE(date, INTERVAL expr unit):与DATE_ADD和DATE_SUB功能相同,只是写法不同。-- 加一天 SELECT ADDDATE('2023-10-27', INTERVAL 1 DAY); -- 输出:2023-10-28 -- 减一个月 SELECT SUBDATE('2023-10-27', INTERVAL 1 MONTH); -- 输出:2023-09-27 -
ADDTIME(datetime, time)和SUBTIME(datetime, time):在日期时间上加减时间。-- 加3小时 SELECT ADDTIME('2023-10-27 10:00:00', '3:0:0'); -- 输出:2023-10-27 13:00:00 -- 减30分钟 SELECT SUBTIME('2023-10-27 10:00:00', '0:30:0'); -- 输出:2023-10-27 09:30:00 -
DATEDIFF(date1, date2): 计算两个日期之间的天数差 (date1 – date2)SELECT DATEDIFF('2023-10-27', '2023-10-20'); -- 输出:7 -
TIMEDIFF(time1, time2): 计算两个时间之间的差 (time1 – time2)SELECT TIMEDIFF('14:30:00', '10:00:00'); -- 输出:04:30:00 -
LAST_DAY(date): 获取指定日期所在月份的最后一天SELECT LAST_DAY('2023-02-15'); -- 输出:2023-02-28
4. 日期时间格式化
-
DATE_FORMAT(date, format): 将日期时间格式化成指定的字符串date: 要格式化的日期时间-
format: 格式字符串,常用的格式符号如下:格式符号 描述 %Y4位的年份 (例如: 2023) %y2位的年份 (例如: 23) %m2位的月份 (01-12) %c月份 (1-12) %d2位的日 (01-31) %e日 (1-31) %H24小时制的小时 (00-23) %h12小时制的小时 (01-12) %i分钟 (00-59) %s秒 (00-59) %pAM 或 PM %W星期几的完整名称 (例如: Sunday) %a星期几的缩写 (例如: Sun) %M月份的完整名称 (例如: January) %b月份的缩写 (例如: Jan)
SELECT DATE_FORMAT('2023-10-27 14:30:00', '%Y-%m-%d %H:%i:%s'); -- 输出:2023-10-27 14:30:00 SELECT DATE_FORMAT('2023-10-27 14:30:00', '%Y年%m月%d日 %H时%i分%s秒'); -- 输出:2023年10月27日 14时30分00秒 SELECT DATE_FORMAT('2023-10-27', '%W, %M %e, %Y'); -- 输出:Friday, October 27, 2023 -
TIME_FORMAT(time, format): 将时间格式化成指定的字符串,格式符号与DATE_FORMAT类似,但是只适用于时间部分。SELECT TIME_FORMAT('14:30:00', '%H:%i:%s'); -- 输出:14:30:00
5. 日期时间转换
-
STR_TO_DATE(str, format): 将字符串转换为日期时间类型str: 要转换的字符串format: 格式字符串,与DATE_FORMAT相同
SELECT STR_TO_DATE('2023-10-27 14:30:00', '%Y-%m-%d %H:%i:%s'); -- 输出:2023-10-27 14:30:00 (DATETIME类型) SELECT STR_TO_DATE('October 27, 2023', '%M %d, %Y'); -- 输出:2023-10-27 (DATE类型)
第三幕:进阶魔法 – 复杂的日期时间计算与格式化
掌握了基本魔法,咱们就可以挑战一些更高级的技巧了。
1. 获取本周、本月、本季度、本年的开始和结束日期
这在报表统计中非常有用。
-- 本周开始日期 (星期一)
SELECT DATE(SUBDATE(CURDATE(),WEEKDAY(CURDATE())));
-- 本周结束日期 (星期日)
SELECT DATE(ADDDATE(CURDATE(),6-WEEKDAY(CURDATE())));
-- 本月开始日期
SELECT DATE(MAKEDATE(YEAR(CURDATE()),1) + INTERVAL (MONTH(CURDATE())-1) MONTH);
SELECT DATE_FORMAT(CURDATE(), '%Y-%m-01'); -- 更简洁的方法
-- 本月结束日期
SELECT LAST_DAY(CURDATE());
-- 本季度开始日期
SELECT MAKEDATE(YEAR(CURDATE()),1) + INTERVAL QUARTER(CURDATE())*3-3 MONTH;
-- 本季度结束日期
SELECT LAST_DAY(MAKEDATE(YEAR(CURDATE()),1) + INTERVAL QUARTER(CURDATE())*3-1 MONTH);
-- 本年开始日期
SELECT DATE_FORMAT(CURDATE(), '%Y-01-01');
-- 本年结束日期
SELECT DATE_FORMAT(CURDATE(), '%Y-12-31');
2. 计算两个日期之间的工作日天数
这个比较复杂,需要自定义函数。
DELIMITER //
CREATE FUNCTION `WORKDAYDIFF`(start_date DATE, end_date DATE)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE workday INT;
DECLARE totaldays INT;
SET workday = 0;
SET totaldays = DATEDIFF(end_date, start_date);
WHILE totaldays >= 0 DO
SET @current_date = DATE_ADD(start_date, INTERVAL workday DAY);
IF DAYOFWEEK(@current_date) BETWEEN 2 AND 6 THEN
SET workday = workday + 1;
END IF;
SET totaldays = totaldays - 1;
END WHILE;
RETURN workday;
END//
DELIMITER ;
-- 使用示例
SELECT WORKDAYDIFF('2023-10-20', '2023-10-27'); -- 输出:5
注意: 这个函数没有考虑节假日,需要根据实际情况进行修改。
3. 根据日期时间进行分组统计
-- 按天统计订单数量
SELECT DATE(order_time), COUNT(*) FROM orders GROUP BY DATE(order_time);
-- 按月统计订单数量
SELECT DATE_FORMAT(order_time, '%Y-%m'), COUNT(*) FROM orders GROUP BY DATE_FORMAT(order_time, '%Y-%m');
-- 按小时统计订单数量
SELECT DATE_FORMAT(order_time, '%Y-%m-%d %H'), COUNT(*) FROM orders GROUP BY DATE_FORMAT(order_time, '%Y-%m-%d %H');
4. 处理时区问题
如果你的应用涉及到多个时区,你需要注意以下几点:
- MySQL服务器的时区设置 (
time_zone系统变量) - 连接客户端的时区设置
TIMESTAMP类型会自动进行时区转换,而DATETIME不会。
可以使用以下函数进行时区转换:
-
CONVERT_TZ(dt, from_tz, to_tz): 将日期时间从一个时区转换为另一个时区。-- 将UTC时间转换为北京时间 SELECT CONVERT_TZ('2023-10-27 10:00:00', '+00:00', '+08:00'); -- 输出:2023-10-27 18:00:00 -
@@session.time_zone: 获取当前会话的时区SELECT @@session.time_zone; -
SET time_zone = '+08:00': 设置当前会话的时区SET time_zone = '+08:00';
第四幕:实战演练 – 几个常见的应用场景
理论学习了这么多,咱们来几个实战演练,看看这些魔法怎么用。
1. 查询最近7天的用户注册数量
SELECT DATE(register_time) AS register_date, COUNT(*) AS user_count
FROM users
WHERE register_time BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE()
GROUP BY register_date
ORDER BY register_date;
2. 查询每个月销售额最高的商品
SELECT
DATE_FORMAT(sale_date, '%Y-%m') AS sale_month,
product_id,
MAX(amount) AS max_amount
FROM sales
GROUP BY sale_month
ORDER BY sale_month;
3. 计算用户平均每月消费金额
SELECT
user_id,
AVG(monthly_amount) AS avg_monthly_amount
FROM (
SELECT
user_id,
DATE_FORMAT(order_time, '%Y-%m') AS order_month,
SUM(amount) AS monthly_amount
FROM orders
GROUP BY user_id, order_month
) AS monthly_sales
GROUP BY user_id;
总结:时间魔法的奥秘
好了,今天的MySQL日期时间函数之旅就到这里了。 记住,时间魔法的关键在于理解各种日期时间类型,掌握常用的日期时间函数,以及灵活运用它们来解决实际问题。 熟能生巧,多练习,你也能成为时间魔法大师!
最后,送大家一句箴言:时间就像海绵里的水,只要你肯挤,总还是有的。 当然,用MySQL的日期时间函数来管理时间,可以让你挤得更轻松!
希望今天的分享对大家有所帮助,咱们下期再见!