MySQL数据库中存储的日期时间数据经常需要根据业务需求进行格式转换,特别是当原始数据以字符串形式存储时。"MySQL 字符串日期格式转换-12"这个主题聚焦于12种常见的字符串日期格式转换场景,涵盖了从基础到进阶的各种转换需求。
在实际项目中,我们经常会遇到以下几种典型情况:
STR_TO_DATE()是MySQL中最重要的字符串转日期函数,其基本语法为:
sql复制STR_TO_DATE(str, format)
其中format参数支持以下常用格式符:
典型应用示例:
sql复制-- 将"2023/03/15"转换为日期类型
SELECT STR_TO_DATE('2023/03/15', '%Y/%m/%d');
-- 处理"15-Mar-2023"格式
SELECT STR_TO_DATE('15-Mar-2023', '%d-%b-%Y');
注意:STR_TO_DATE()对格式字符串非常敏感,如果格式不匹配会返回NULL而非报错,建议总是检查返回值。
DATE_FORMAT()用于将日期类型格式化为指定字符串,是STR_TO_DATE()的逆操作:
sql复制DATE_FORMAT(date, format)
常用格式化符号:
实用案例:
sql复制-- 将当前日期格式化为"March 15, 2023"
SELECT DATE_FORMAT(NOW(), '%M %d, %Y');
-- 输出"2023Q1"这样的季度表示
SELECT CONCAT(YEAR(NOW()), 'Q', QUARTER(NOW()));
sql复制-- "2023-03-15"转日期
SELECT STR_TO_DATE('2023-03-15', '%Y-%m-%d');
-- 反向转换
SELECT DATE_FORMAT(CURDATE(), '%Y-%m-%d');
sql复制-- "2023/03/15"处理
SELECT STR_TO_DATE('2023/03/15', '%Y/%m/%d');
sql复制-- "2023-03-15 14:30:00"处理
SELECT STR_TO_DATE('2023-03-15 14:30:00', '%Y-%m-%d %H:%i:%s');
sql复制-- "15-March-2023"转日期
SELECT STR_TO_DATE('15-March-2023', '%d-%M-%Y');
-- 支持缩写月份名
SELECT STR_TO_DATE('15-Mar-2023', '%d-%b-%Y');
sql复制-- 处理"15th March 2023"格式
SELECT STR_TO_DATE(
REPLACE(REPLACE(REPLACE(REPLACE('15th March 2023',
'st', ''), 'nd', ''), 'rd', ''), 'th', ''),
'%d %M %Y'
);
sql复制-- 处理"2023|03|15"格式
SELECT STR_TO_DATE(REPLACE('2023|03|15', '|', '-'), '%Y-%m-%d');
sql复制-- 尝试多种格式直到成功
SELECT COALESCE(
STR_TO_DATE('03/15/2023', '%m/%d/%Y'),
STR_TO_DATE('15/03/2023', '%d/%m/%Y'),
STR_TO_DATE('2023/03/15', '%Y/%m/%d')
);
sql复制-- 时间戳转日期
SELECT FROM_UNIXTIME(1678892400);
-- 日期转时间戳
SELECT UNIX_TIMESTAMP('2023-03-15 00:00:00');
sql复制-- 带时区的时间转换
SELECT CONVERT_TZ('2023-03-15 12:00:00', '+00:00', '+08:00');
sql复制-- 将日期转换为财年格式(假设财年从4月开始)
SELECT
CASE
WHEN MONTH(date_column) >= 4 THEN CONCAT(YEAR(date_column), '-', YEAR(date_column)+1)
ELSE CONCAT(YEAR(date_column)-1, '-', YEAR(date_column))
END AS financial_year
FROM your_table;
sql复制-- 获取ISO周数和年份
SELECT YEARWEEK('2023-03-15', 3); -- 模式3表示ISO标准
-- 周数转日期范围
SELECT
STR_TO_DATE(CONCAT('2023', ' Monday'), '%x %v %W') AS week_start,
DATE_ADD(STR_TO_DATE(CONCAT('2023', ' Monday'), '%x %v %W'), INTERVAL 6 DAY) AS week_end
sql复制-- 临时更改lc_time_names以支持其他语言
SET lc_time_names = 'zh_CN';
SELECT DATE_FORMAT(NOW(), '%M %d, %Y'); -- 输出中文月份名
SET lc_time_names = 'ja_JP';
SELECT DATE_FORMAT(NOW(), '%M %d, %Y'); -- 输出日文月份名
在WHERE子句中对日期列使用函数会导致索引失效:
sql复制-- 错误示例(索引失效)
SELECT * FROM orders
WHERE DATE_FORMAT(order_date, '%Y-%m-%d') = '2023-03-15';
-- 正确做法
SELECT * FROM orders
WHERE order_date BETWEEN '2023-03-15 00:00:00' AND '2023-03-15 23:59:59';
对于大规模数据迁移,建议:
sql复制-- 批量转换示例
INSERT INTO target_table(new_date_column)
SELECT STR_TO_DATE(original_string, '%Y-%m-%d')
FROM source_table
WHERE STR_TO_DATE(original_string, '%Y-%m-%d') IS NOT NULL;
建议添加数据验证步骤:
sql复制-- 创建验证视图
CREATE VIEW invalid_dates AS
SELECT id, date_string
FROM source_table
WHERE STR_TO_DATE(date_string, '%Y-%m-%d') IS NULL
AND date_string IS NOT NULL;
典型时区问题表现:
解决方法:
sql复制-- 查看当前时区设置
SELECT @@global.time_zone, @@session.time_zone;
-- 设置会话时区
SET time_zone = '+08:00';
慢查询可能原因:
优化方案:
sql复制-- 添加函数索引(MySQL 8.0+)
ALTER TABLE orders ADD INDEX idx_order_date_str ((DATE_FORMAT(order_date, '%Y-%m-%d')));
-- 使用生成列
ALTER TABLE orders ADD COLUMN order_date_str VARCHAR(10)
GENERATED ALWAYS AS (DATE_FORMAT(order_date, '%Y-%m-%d')) STORED,
ADD INDEX idx_order_date_str (order_date_str);
销售报表需求:按"YYYY年MM月"格式分组统计
sql复制SELECT
DATE_FORMAT(order_date, '%Y年%m月') AS month,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y年%m月')
ORDER BY MIN(order_date);
处理多种用户日期输入格式:
sql复制CREATE FUNCTION normalize_date(input_str VARCHAR(20))
RETURNS DATE
DETERMINISTIC
BEGIN
DECLARE result DATE;
SET result = STR_TO_DATE(input_str, '%Y-%m-%d');
IF result IS NOT NULL THEN RETURN result; END IF;
SET result = STR_TO_DATE(input_str, '%m/%d/%Y');
IF result IS NOT NULL THEN RETURN result; END IF;
SET result = STR_TO_DATE(input_str, '%d-%b-%Y');
IF result IS NOT NULL THEN RETURN result; END IF;
RETURN NULL;
END;
解析Apache日志日期格式:
sql复制-- 示例日志行:10.0.0.1 - - [15/Mar/2023:10:30:45 +0800]
SELECT
STR_TO_DATE(
SUBSTRING(log_line,
LOCATE('[', log_line) + 1,
LOCATE(']', log_line) - LOCATE('[', log_line) - 1
),
'%d/%b/%Y:%H:%i:%s'
) AS log_time
FROM access_log;
在处理MySQL日期字符串转换时,我发现最常出现的问题是格式字符串的细微差别导致转换失败。一个实用的调试技巧是先用SELECT语句测试转换逻辑,确认无误后再应用到UPDATE或INSERT语句中。对于复杂的转换需求,建议创建存储函数封装转换逻辑,提高代码复用性和可维护性。