Mysql where in 的范围非常多时如何优化?

问题描述:
数据库Mysql8,某个单表几百万的数据量,产品层面上做分页查询,业务上存在select count(*) from records where sale_id in (list) and create_time between start and end
select * from records where sale_id in (list) and create_time between start and end order by create_time desc limit 0, 10的需求,list的数量可能会非常多,可能会几千个,导致查询非常慢,接口超时。

之所以list非常多是因为,选项是个多级树形结构,产品层面可以跨级多选父级,代码在做查询的时候会把选中的sale_id统一转化为叶子节点这一级,叶子总量有1W+, 目前如果只勾选1个1级节点,list有几千个,查询需要6s。

考虑过冗余记录 parent_id序列,但是这个层级对应关系会时长发生变动,如果冗余记录parent_id序列,对应关系变动时会引发大批量的历史数据的更新。

请问目前这种问题应当如何进行优化?

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
最佳答案

试了两种方法有效

1.加sale_id和create_time的复合索引
2.把sale_id改成varchar
1年前 评论
aliongkk 10个月前
小猪蹄子 (作者) 10个月前
不高兴就喝水 10个月前
小猪蹄子 (作者) 10个月前
讨论数量: 22
GDDD

file

1年前 评论
Complicated 1年前

where in 最好不要超过100 条数据 多了最好用join

1年前 评论
穿过你的黑发的我的手 (楼主) 1年前
lijizheng (作者) 1年前
lijizheng (作者) 1年前

laravel orm 自动会把 id 罗列出来 这种数据量大了就不合适了

1年前 评论

临时表:

创建一个临时表并将所有的值插入到这个表中。
使用JOIN或EXISTS子句来代替IN,以便与临时表进行比较。

CREATE TEMPORARY TABLE temp_values (id INT PRIMARY KEY);
INSERT INTO temp_values VALUES (...);

SELECT * FROM target_table
WHERE EXISTS (SELECT 1 FROM temp_values WHERE temp_values.id = target_table.id);
1年前 评论
周小云 (作者) 1年前

试了两种方法有效

1.加sale_id和create_time的复合索引
2.把sale_id改成varchar
1年前 评论
aliongkk 10个月前
小猪蹄子 (作者) 10个月前
不高兴就喝水 10个月前
小猪蹄子 (作者) 10个月前

whereIn(sql语句)

10个月前 评论
sanders

建议将 list 改为子查询,并将为其关联的字段添加索引。我看最佳答案是将字段改为 varchar ,这个我不苟同。

10个月前 评论
小猪蹄子 10个月前

in 里面的数据过长,会截断的,我遇到过,本地测试没问题,上线就出问题了

10个月前 评论
keyboby 10个月前

补充几点说明:
1、选为答案是因为联合索引,index range scan能把查询提升到可接受的时间范围内。改varchar的部分不建议,数字用varchar存的时候,查询时如果写成int而不写成字符串的形式会触发隐式转化不走索引的。
2、关于where in有长度限制的,我没查到相关的说明。能查到的只有mysql的max_allowed_packet参数和PDO的PDO::MYSQL_ATTR_MAX_BUFFER_SIZE参数能影响SQL长度。
3、遇到的问题仅仅只是where in 数千个的问题,并不涉及两个大表之间的join,所以并不适用临时表的方式,即例子中order和order_item的查询。

9个月前 评论

个人的优化, 使用了 子查询 mysql5.6 表: customer 8595750条数据 主键 customer_id

原sql

SELECT `name`,`customer_id`,`nature`,`contacts_id` FROM `5k_customer` WHERE ( `customer_id` IN (18,19,20,30,.....) ORDER BY customer_id desc LIMIT 0,100  

查询结果时间: 2.079s

新sql

SELECT `name`,T.customer_id,`nature`,`contacts_id` FROM `5k_customer` INNER JOIN (select 18 as customer_id union all select 19 union all select 20....)  T ON T.customer_id = 5k_customer.customer_id ORDER BY customer_id desc LIMIT 0,100  

查询结果时间: 1.565s

2个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!