1. MySQL函数概述与使用场景
MySQL作为最流行的关系型数据库之一,其内置函数库是数据处理的核心工具集。这些函数可以大致分为日期时间、字符串处理、数学计算、系统信息等几大类,几乎覆盖了日常开发中的所有数据处理需求。
在实际项目中,我经常看到开发者重复造轮子——在应用层代码中实现本可以由数据库函数完成的操作。这不仅增加了代码复杂度,还可能导致性能问题。比如计算两个日期的天数差,完全可以用DATEDIFF()函数在SQL层面解决,没必要把数据取到应用层再计算。
提示:数据库函数执行效率通常高于应用层处理,因为减少了数据传输开销,且MySQL对内置函数有专门优化。
2. 日期时间函数深度解析
2.1 基础日期时间函数
最常用的日期函数包括:
- NOW():获取当前完整的日期时间(如'2023-08-20 14:30:15')
- CURDATE()/CURRENT_DATE():仅获取日期部分
- CURTIME()/CURRENT_TIME():仅获取时间部分
这些函数在记录创建时间、更新时间等场景非常实用。例如电商订单表通常会这样定义:
CREATE TABLE orders ( id INT PRIMARY KEY, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );2.2 日期计算与转换
DATE_ADD()和DATE_SUB()是日期运算的核心函数,支持多种时间单位:
-- 三天后的日期 SELECT DATE_ADD(NOW(), INTERVAL 3 DAY); -- 两小时前的时间 SELECT DATE_SUB(NOW(), INTERVAL 2 HOUR);DATEDIFF()计算日期差时有个易错点:它是用第一个日期减第二个日期。在计算会员有效期时要注意顺序:
-- 计算会员剩余天数(错误示范) SELECT DATEDIFF(expire_date, NOW()) FROM members; -- 正确写法(NOW在前会得到负数) SELECT DATEDIFF(NOW(), expire_date) FROM members;2.3 日期格式化与提取
DATE_FORMAT()函数支持灵活的日期显示格式:
-- 显示为"2023年08月20日" SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日'); -- 提取季度信息 SELECT QUARTER('2023-08-20'); -- 返回3经验:在报表统计中,经常需要按周、月、季度分组,可以用这些函数预处理:
SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, COUNT(*) AS order_count FROM orders GROUP BY month;3. 字符串处理函数实战技巧
3.1 字符串基本操作
CONCAT()的进阶用法是处理可能为NULL的值:
-- 安全拼接可能为NULL的字段 SELECT CONCAT(IFNULL(first_name, ''), IFNULL(last_name, '')) FROM users;SUBSTRING()的参数需要注意:
- 起始位置从1开始
- 不指定长度时截取到字符串末尾
- 支持负数位置(从末尾倒数)
3.2 字符串搜索与替换
INSTR()和LOCATE()都可以查找子串位置,但参数顺序不同:
-- 查找@在邮箱中的位置 SELECT INSTR('user@example.com', '@'); -- 返回5 SELECT LOCATE('@', 'user@example.com'); -- 同上REPLACE()在数据清洗时特别有用:
-- 统一替换手机号中间四位 UPDATE users SET phone = CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4));3.3 字符集与长度处理
LENGTH()和CHAR_LENGTH()的区别:
-- UTF8中文测试 SELECT LENGTH('中文'), -- 返回6(字节数) CHAR_LENGTH('中文'); -- 返回2(字符数)踩坑记录:曾经有个BUG是因为用LENGTH()判断中文字符串长度导致的,应该用CHAR_LENGTH()。
4. 数学函数在业务中的应用
4.1 数值处理函数
ROUND()的银行家舍入规则要注意:
SELECT ROUND(2.5); -- 返回2(不是3!) SELECT ROUND(3.5); -- 返回4FORMAT()函数会返回带千分位的字符串:
SELECT FORMAT(1234567.89, 2); -- 返回"1,234,567.89"4.2 随机数与抽样
RAND()结合ORDER BY可以实现随机抽样:
-- 随机选取10个用户 SELECT * FROM users ORDER BY RAND() LIMIT 10;性能警告:大数据表慎用RAND(),可以考虑先获取ID范围再随机。
4.3 进制转换函数
BIN()、HEX()、CONV()用于不同进制转换:
-- IP地址转十进制 SELECT CONV(HEX('192.168.1.1'), 16, 10);5. 高级函数与性能优化
5.1 条件判断函数
CASE WHEN是更灵活的条件表达式:
SELECT name, CASE WHEN score >= 90 THEN 'A' WHEN score >= 80 THEN 'B' ELSE 'C' END AS grade FROM students;5.2 聚合函数增强
GROUP_CONCAT()实现多行合并:
-- 每个部门的员工名单 SELECT department, GROUP_CONCAT(name SEPARATOR ', ') AS members FROM employees GROUP BY department;5.3 函数使用性能建议
- 避免在WHERE条件中使用函数:
-- 反例(无法使用索引) SELECT * FROM logs WHERE DATE_FORMAT(create_time, '%Y-%m') = '2023-08'; -- 正例 SELECT * FROM logs WHERE create_time >= '2023-08-01' AND create_time < '2023-09-01';- 计算列考虑使用持久化计算列(MySQL 5.7+):
ALTER TABLE products ADD COLUMN price_tax DECIMAL(10,2) AS (price * 1.1) STORED;6. 实战案例:电商系统函数应用
6.1 订单超时处理
-- 查询30分钟未支付的订单 SELECT * FROM orders WHERE status = 'unpaid' AND create_time < DATE_SUB(NOW(), INTERVAL 30 MINUTE); -- 批量取消超时订单 UPDATE orders SET status = 'canceled' WHERE status = 'unpaid' AND create_time < DATE_SUB(NOW(), INTERVAL 30 MINUTE);6.2 用户画像分析
-- 用户年龄分段统计 SELECT CASE WHEN age < 18 THEN '未成年' WHEN age BETWEEN 18 AND 25 THEN '18-25' WHEN age BETWEEN 26 AND 35 THEN '26-35' ELSE '35+' END AS age_group, COUNT(*) AS user_count FROM users GROUP BY age_group;6.3 商品搜索优化
-- 模糊搜索时先去除空格 SELECT * FROM products WHERE REPLACE(name, ' ', '') LIKE '%智能手机%';7. 跨数据库兼容性处理
虽然大多数SQL函数是通用的,但存在一些差异:
- 日期格式化:
- MySQL:DATE_FORMAT(date, '%Y-%m-%d')
- Oracle:TO_CHAR(date, 'YYYY-MM-DD')
- SQL Server:FORMAT(date, 'yyyy-MM-dd')
- 字符串连接:
- MySQL:CONCAT(str1, str2)
- Oracle:str1 || str2
- SQL Server:str1 + str2
- 分页查询:
- MySQL:LIMIT offset, count
- Oracle:ROWNUM
- SQL Server:OFFSET-FETCH
建议在应用层使用ORM工具或抽象出SQL构建器来处理这些差异。