问题概述
平时Mysql喜欢先sql语句找出id字符串,然后用in后面接(id,id,……)去查找数据。查阅相关文章,说sql语句长度有限制,随着数据增长就不能执行,但是 这个长度却很大,一般可以不用考虑。
查看方法。
mysql>show variables like '%max_allowed_pack%'
+--------------------------+-----------------+
| Variable_name | Value |
+--------------------------+-----------------+
| max_allowed_packet | 1073741824 |
| slave_max_allowed_packet | 1073741824 |
+--------------------------+-----------------+
返回行数:[2],耗时:7 ms.
根据返回结果可以看出,这个最大长度是很大的。
IN相关操作和替代方法对比
1、IN后面接主键字符串( 概念错误SELECT GROUP_CONCAT(order_id,’’) FROM order_action WHERE NAME = ‘user_add’ 查找出来的并不是字符串,而是数据集 )
代码
EXPLAIN SELECT
*
FROM
`order`
WHERE
id IN ( SELECT GROUP_CONCAT(order_id,'') FROM order_action WHERE NAME = 'user_add' )
结果显示,order表需要找全部行,order_action也是。
+----+--------------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | order | NULL | ALL | NULL | NULL | NULL | NULL | 17239 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | order_action | NULL | ALL | NULL | NULL | NULL | NULL | 20660 | 10.00 | Using where |
+----+--------------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)
2、in 后面接 select 数据集
代码
EXPLAIN SELECT
*
FROM
`order`
WHERE
id IN ( SELECT order_id FROM order_action WHERE NAME = 'user_add' )
结果显示,会创建临时表
+----+--------------+--------------+------------+--------+---------------+---------+---------+----------------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+--------------+------------+--------+---------------+---------+---------+----------------------+-------+----------+-------------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL |
| 1 | SIMPLE | order | NULL | eq_ref | PRIMARY | PRIMARY | 4 | <subquery2>.order_id | 1 | 100.00 | NULL |
| 2 | MATERIALIZED | order_action | NULL | ALL | NULL | NULL | NULL | NULL | 20649 | 10.00 | Using where |
+----+--------------+--------------+------------+--------+---------------+---------+---------+----------------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
3、用 join 替换 in
代码
EXPLAIN SELECT
*
FROM
`order` AS ot
INNER JOIN order_action oat ON oat.order_id = ot.id
AND oat.NAME = 'user_add'
结果显示,不创建临时表,但order表只需要查1rows,order_action表查所有行。
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+-------+----------+-------------+
| 1 | SIMPLE | oat | NULL | ALL | NULL | NULL | NULL | NULL | 20653 | 10.00 | Using where |
| 1 | SIMPLE | ot | NULL | eq_ref | PRIMARY | PRIMARY | 4 | sima.oat.order_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
4、exist 替代 in (效率不行)
代码
EXPLAIN SELECT
*
FROM
`order` AS ot
WHERE
EXISTS ( SELECT order_id FROM order_action oat WHERE ot.id = oat.order_id AND NAME = 'user_add' )
结果测试的是两张关联的表都查了所有。所以exist效率不行。
+----+--------------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | ot | NULL | ALL | NULL | NULL | NULL | NULL | 17233 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | oat | NULL | ALL | NULL | NULL | NULL | NULL | 20656 | 1.00 | Using where |
+----+--------------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec)
explain 分析 sql 语句的相关属性解析
连接操作的TYPE类型
(1)SYSTEM
CONST的特例,当表上只有一条元组匹配
(2)CONST
WHERE条件筛选后表上至多有一条元组匹配时,比如WHERE ID = 2 (ID是主键,值为2的要么有一条要么没有)
(3)EQ_REF
参与连接运算的表是内表(在代码实现的算法中,两表连接时作为循环中的内循环遍历的对象,这样的表称为内表)。 基于索引(连接字段上存在唯一索引或者主键索引,且操作符必须是“=”谓词,索引值不能为NULL)做扫描,使得对外表的一条元组,内表只有唯一一条元组与之对应。
(4)REF
可以用于单表扫描或者连接。参与连接运算的表,是内表。 基于索引(连接字段上的索引是非唯一索引,操作符必须是“=”谓词,连接字段值不可为NULL)做扫描,使得对外表的一条元组,内表可有若干条元组与之对应。
(5)REF_OR_NULL
类似REF,只是搜索条件包括:连接字段的值可以为NULL的情况,比如 where col = 2 or col is null
(6)RANGE
范围扫描,基于索引做范围扫描,为诸如BETWEEN,IN,>=,LIKE类操作提供支持
(7)INDEX_SCAN
索引做扫描,是基于索引在索引的叶子节点上找满足条件的数据(不需要访问数据文件)
(8)ALL
全表扫描或者范围扫描:不使用索引,顺序扫描,直接读取表上的数据(访问数据文件)
(9)UNIQUE_SUBQUERY
在子查询中,基于唯一索引进行扫描,类似于EQ_REF
(10)INDEX_SUBQUERY
在子查询中,基于除唯一索引之外的索引进行扫描
(11)INDEX_MERGE
多重范围扫描。两表连接的每个表的连接字段上均有索引存在且索引有序,结果合并在一起。适用于作集合的并、交操作。
(12)FT
FULL TEXT,全文检索
结论
实际测试显示,in数据集是最快的。所有优先考虑in数据集这种会创建临时表的方式。
参考
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 chunpat 及本文源链接。