发布于2026-07-06 阅读(0)
扫一扫,手机访问
先说几个核心判断:BETWEEN 比 IN 更适合数值和时间范围的筛选,前提是字段有索引;而IN只适用于离散值匹配,而且值越多,性能越差。这不是什么高深理论,但在实际项目中,偏偏有太多人栽在这上面。

ThinkPHP 的 whereBetween('created_at', [$start, $end]) 生成的是 BETWEEN ? AND ?,MySQL 可以借助 B-Tree 索引快速定位区间的起点和终点。而 whereIn('id', [6,7,8,9,10]) 本质上被展开成 id IN (6,7,8,9,10),即使有索引,也要做多次等值查找,没法利用索引的有序性。
whereBetween()whereIn(),再多就该考虑分页或者临时表了whereBetween() 对字符串字段也有效,比如 whereBetween('status', ['draft', 'published']),但前提是字段有索引,且字符集支持范围比较很多人写了 whereBetween('updated_at', ['2025-01-01', '2025-12-31']) 却发现慢,不是方法不对,而是 updated_at 根本没建索引。MySQL 不会自动为 WHERE 字段加索引,ThinkPHP 更不会。这事儿得靠自己。
SHOW INDEX FROM user,确认 updated_at 出现在结果中ALTER TABLE user ADD INDEX idx_updated_at (updated_at)where(['status' => 'active', 'updated_at' => ['between', $range]]))应该建联合索引:ADD INDEX idx_status_updated (status, updated_at),顺序不能搞反where('DATE(updated_at)', '2025-01-01'),改用 whereBetween('updated_at', ['2025-01-01 00:00:00', '2025-01-01 23:59:59'])MySQL 对 IN 列表长度没有硬性限制,但值一旦太多,解析会变慢,执行计划可能失效,甚至触发 max_allowed_packet 错误。线上最常见的是导出筛选或者后台批量操作。
chunk() 处理:UserModel::whereIn('id', $idList)->chunk(200, function ($users) { ... })INSERT INTO temp_ids SELECT UNHEX(?) 批量写入,再 JOIN temp_ids 查询,适合 ≥ 1000 个值的情况whereIn() 的字符串拼接模式:配置 'parse_in_condition' => false(TP6.1+),强制走预处理,避免 SQL 注入风险和解析开销whereNotIn() 无法用索引优化,大数据量下要慎用,可以改写为 LEFT JOIN ... IS NULL光盯着 ThinkPHP 的 getLastSql() 看是没用的,必须执行 EXPLAIN 看 type 和 key 字段。太多人“以为走了索引”,实际结果是 type: ALL 或 key: NULL。
'sql_explain' => true,每条 SELECT 会自动打印 EXPLAIN 结果type 应为 range(BETWEEN)或 ref(IN),key 显示索引名,rows 越小越好Extra 出现 Using filesort 或 Using temporary,说明排序或分组没走索引,需要调整字段顺序或加覆盖索引Db::name('user')->cache(false)->whereBetween(...)->select(),避免缓存掩盖真实的耗时说到底,真正卡住的从来不是语法怎么写,而是索引建没建、EXPLAIN 看没看、值列表长度控没控——这三件事漏掉任何一件,whereBetween() 和 whereIn() 都只是看起来优雅的慢查询而已。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8