推荐使用 DATE() 而非 DATE_FORMAT(),因 DATE() 可走索引,而 DATE_FORMAT() 导致全表扫描;最优写法是 WHERE create_time >= '2024-05-20' AND create_time
WHERE 条件里用
DATE()还是DATE_FORMAT()?直接用
DATE()更安全。它把DATETIME或TIMESTAMP截成纯日期(如'2024-05-20'),能走索引;而DATE_FORMAT(col, '%Y-%m-%d')是字符串格式化,结果不可索引,WHERE 中一用就全表扫描。常见错误:写成
WHERE DATE_FORMAT(create_time, '%Y-%m-%d') = '2024-05-20'—— 看似直观,实则慢。
- 推荐写法:
WHERE DATE(create_time) = '2024-05-20'- 更优写法(索引友好):
WHERE create_time >= '2024-05-20' AND create_timeDATE_FORMAT()适合 SELECT 输出美化,比如SELECT DATE_FORMAT(update_time, '%m/%d %H:%i') AS time_str
GROUP BY后怎么统计「当天第几次下单」?用ROW_NUMBER()还是变量?MySQL 8.0+ 直接用窗口函数
ROW_NUMBER(),清晰且可靠;5.7 及以下只能靠用户变量,但要注意执行顺序不稳定,容易出错。示例(按用户、按天编号订单):
SELECT user_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id, DATE(order_time) ORDER BY order_time) AS seq FROM orders;
- 别用
@row := @row + 1模拟,ORDER BY 和变量赋值顺序在不同版本行为不一致PARTITION BY必须包含DATE(order_time),不能只写order_time,否则跨天不重置- 如果要兼容老版本,优先考虑应用层排序编号,避免 SQL 层强依赖变量
字符串拼接选
CONCAT()还是CONCAT_WS()?空值怎么处理?
CONCAT()遇到任意参数为NULL就返回NULL;CONCAT_WS()(WS = With Separator)会跳过NULL参数,更适合拼地址、姓名等易为空的字段。典型场景:拼用户全名,中间名可能为空。
- 危险写法:
CONCAT(last_name, ', ', middle_name, ' ', first_name)→ 一有middle_name IS NULL,整列变NULL- 稳妥写法:
CONCAT_WS(' ', last_name, middle_name, first_name)→ 自动忽略NULL,结果如'Zhang Li'- 需要逗号分隔时:
CONCAT_WS(', ', city, province, country),比嵌套IFNULL()干净得多
COUNT()统计时,COUNT(*)、COUNT(1)、COUNT(col)有啥区别?绝大多数情况下,
COUNT(*)和COUNT(1)性能完全一样,优化器都当作“数行”处理;但COUNT(col)只统计该列非NULL的行数,语义不同,不能混用。常见误解:
COUNT(1)比COUNT(*)快 —— 实测无差别,别被过时经验误导。
- 统计总行数(含 NULL 行):必须用
COUNT(*)(语义明确,官方推荐)- 统计某列有值的行数:用
COUNT(status),等价于COUNT(CASE WHEN status IS NOT NULL THEN 1 END)- 想避免全表扫描?加覆盖索引,而不是换函数写法
真正容易被忽略的是:在
LEFT JOIN后对右表字段用COUNT(right_col),会把没匹配的行算作 0,但COUNT(*)仍计左表所有行——这里语义差异直接影响业务逻辑,调错一次就漏数据。












