mysql慢查询之同一个字段做两次排序的思考

线上业务使用到了ORM框架,发现了一个慢查询sql,同一个字段做了两次排序,导致产生了filesort。
ORM框架自带的表结构如下:

CREATE TABLE `unopen` (
  `corp` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `invtype` tinyint(3) NOT NULL DEFAULT '1',
  `uid` int(10) NOT NULL DEFAULT '0',
  `username` char(32) NOT NULL DEFAULT '0',
  `fatid` int(10) NOT NULL DEFAULT '0',
  `fatname` char(32) NOT NULL DEFAULT '0',
  `invtitle` char(100) NOT NULL DEFAULT '',
  `invest` decimal(12,2) NOT NULL DEFAULT '0.00',
  `opened` decimal(12,2) unsigned NOT NULL DEFAULT '0.00',
  `unopened` decimal(12,2) NOT NULL DEFAULT '0.00',
  `doflag` tinyint(3) NOT NULL DEFAULT '0',
  `manual_opened` decimal(12,2) NOT NULL DEFAULT '0.00',
  PRIMARY KEY (`uid`,`corp`,`doflag`,`invtype`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
看到这个表结构就会想到第一件事就想增加自增主键(有点强迫症的感觉),其实表不一定要一个自增整型的字段来做主键,只要是能保证唯一的字段都可以用来做主键,最好是整型。但主要还得看应用场景,只有等值查询的列,或多列来创建单列主键或联合主键,可能会更好,因为如果作为二级索引存在时,可能还会造成回表查询,直接是主键可直接通过主键拿回数据。 回到正题,原sql是如下这样的:

SELECT `i`.*, `v`.`type`, `v`.`status` FROM `V_STAT`.`unopen` AS `i`
 LEFT JOIN `V_USER`.`info` AS `v` ON `i`.`uid` = `v`.`uid` WHERE (`i`.`uid` > 0) AND (`i`.`corp` = '31') AND (`i`.`doflag` = '2') ORDER BY `uid` ASC, `uid` ASC, `corp` ASC LIMIT 50;
这个sql执行需要2.5s多,explain一看进行了filesort

+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+-----------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref           | rows    | Extra                       |
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+-----------------------------+
|  1 | SIMPLE      | i     | range | PRIMARY       | PRIMARY | 4       | NULL          | 1610297 | Using where; Using filesort |
|  1 | SIMPLE      | v     | ref   | PRIMARY       | PRIMARY | 4       | V_STAT.i.uid |       1 |                             |
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+-----------------------------+
根据表的结构及sql,都使用了索引字段查询排序,且排序字段都是基表字段,应该不需要再排序了,对如何让group by不产生sort可以参考下【mysql】创建索引时如何考虑order by查询。在调整单引号时,发现这个sql出现了两次uid的排序,而且没有明确标明是哪个表的。意思应该是使用i.uid和v.uid进行排序,于是我就加上。

mysql> explain SELECT  `i`.*, `v`.`type`, `v`.`status` FROM `V_STAT`.`unopen` AS `i`  LEFT JOIN `V_USER`.`info` AS `v` ON `i`.`uid` = `v`.`uid` WHERE (`i`.`uid` > 0) AND (`i`.`corp` = 3) AND (`i`.`doflag` = 2) ORDER BY i.`uid` ASC, v.`uid` ASC, i.`corp` ASC LIMIT 50;
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+----------------------------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref           | rows    | Extra                                        |
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+----------------------------------------------+
|  1 | SIMPLE      | i     | range | PRIMARY       | PRIMARY | 4       | NULL          | 1610297 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | v     | ref   | PRIMARY       | PRIMARY | 4       | V_STAT.i.uid |       1 |                                              |
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+----------------------------------------------+
发现了using temporary,当没有指定排序表时,默认走i表的uid了。其实这里已经很明显,这个uid是不需要做两次排序的,因为两个表的uid是相同的,取出来时顺序肯定是一样的。果断地删除后面的uid 排序,并明确排序表为i,这里只为增加可读性。

mysql> explain SELECT  `i`.*, `v`.`type`, `v`.`status` FROM `V_STAT`.`unopen` AS `i`  LEFT JOIN `V_USER`.`info` AS `v` ON `i`.`uid` = `v`.`uid` WHERE (`i`.`uid` > 0) AND (`i`.`corp` = 3) AND (`i`.`doflag` = 2) ORDER BY i.`uid` ASC, i.`corp` ASC LIMIT 50;
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref           | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+-------------+
|  1 | SIMPLE      | i     | range | PRIMARY       | PRIMARY | 4       | NULL          | 1610297 | Using where |
|  1 | SIMPLE      | v     | ref   | PRIMARY       | PRIMARY | 4       | V_STAT.i.uid |       1 |             |
+----+-------------+-------+-------+---------------+---------+---------+---------------+---------+-------------+
filesort消失,查询直接变为ms级。

时间: 2024-10-08 15:20:18

mysql慢查询之同一个字段做两次排序的思考的相关文章

多对多关系多表连接查询,同一个字段同时满足多个条件

问题描述 分类表t_keytype商品表t_product分类商品关联表t_typeproducttid(分类ID)name(分类名称)pid(商品ID)name(商品名称)idpid(商品ID)tid(分类ID)1分类11商品11112分类22商品2212321422现在要表连接查询,根据分类ID字段同时满足多个条件的商品"(分类id=1and分类id=2)"查询是同一个字段多个and,请问改怎么写,尽量不要子查询因为数据量比较大,在此谢谢各位啦! 解决方案 解决方案二:网了发图,如

Mysql查询指定某字段顺序排序的两种方法

Mysql查询按照某字段指定顺序排序的两个方法,FIELD和substring_index.  代码如下 复制代码 SELECT * FROM documents WHERE id IN (5,3,6,1) ORDER BY FIELD(id,5,3,6,1); 或者:  代码如下 复制代码 SELECT * FROM documents WHERE id IN (5,3,6,1) ORDER BY substring_index((id,5,3,6,1),id,1);

MySql查询不区分大小写解决方案(两种)

当我们输入不管大小写都能查询到数据,例如:输入 aaa 或者aaA ,AAA都能查询同样的结果,说明查询条件对大小写不敏感. 解决方案一: 于是怀疑Mysql的问题.做个实验:直接使用客户端用sql查询数据库. 发现的确是大小不敏感 . 通过查询资料发现需要设置collate(校对) . collate规则: *_bin: 表示的是binary case sensitive collation,也就是说是区分大小写的  *_cs: case sensitive collation,区分大小写  

mysql 怎么查询整个数据库中某个特定值所在的表和字段?

问题描述 mysql 怎么查询整个数据库中某个特定值所在的表和字段? 比如说有 person表中有name="苹果",goods表中有label="苹果" 那么根据"苹果"怎么确认person 与 goods这两张表及各自对应的是name与label字段呢? 解决方案 查询整个数据库中某个特定值所在的表和字段的方法查询整个数据库中某个特定值所在的表和字段的方法查询整个数据库中某个特定值所在的表和字段的方法 解决方案二: 如果,你说的Pseron里

mysql update 根据表中字段查询另一张表更新更新

问题描述 mysql update 根据表中字段查询另一张表更新更新 mysql有两张表, 班级表class,包含 | id | name | | 1 | 一班 | | 2 | 二班 | 学生表student,其中classId为空,className有值并对应class表中的name | id | name | classId | className | | 1 | 一班 | | 一班 | | 2 | 二班 | | 二班 | | 3 | 一班 | | 一班 | | 4 | 二班 | | 二班

mysql查询,因为字段设计有差异

问题描述 mysql查询,因为字段设计有差异 有2表,1张短信表(发送量int,时间timestamp), 1张流量表(流量数varchar,时间bigint) 求2015年每月短信总数和流量总数 解决方案 MySQL字段拼接查询 解决方案二: 做类型转化啊:: 因为你并没有说存的时间是什么时候所以我就默认为天 第一张就是 select sum(短信) from 短信表 where to_char(时间) like ''%找出当月所有时间段的共同点%//得到的是短信总和 第二张表是 select

数据-Mysql查询出现BLOB字段

问题描述 Mysql查询出现BLOB字段 我在做一个学校的学生数据录入,一共有1200+的班级学生表,用select union语句 把所有学生输出到屏幕时,班级编号栏出现blob,navicat下对blob点击备注,班级 编号是可以从备注显示的, 而我如果select union到第1064张班级表,班级编号栏是显示正常的,请问这是什么原因呢??

mysql批量更新多条记录的同一个字段为不同值的方法_Mysql

首先mysql更新数据的某个字段,一般这样写: UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value'; 也可以这样用in指定要更新的记录: UPDATE mytable SET myfield = 'value' WHERE other_field in ('other_values'); 这里注意 'other_values' 是一个逗号(,)分隔的字符串,如:1,2,3 如果更新多条数据而且每条记录要更新

MySQL大表中重复字段的高效率查询方法

MySQL大表重复字段应该如何查询到呢?这是很多人都遇到的问题,下面就教您一个MySQL大表重复字段的查询方法,供您参考. 数据库中有个大表,需要查找其中的名字有重复的记录id,以便比较. 如果仅仅是查找数据库中name不重复的字段,很容易 代码如下: SELECT min(`id`),`name` FROM `table` GROUP BY `name`; 但是这样并不能得到说有重复字段的id值.(只得到了最小的一个id值) 查询哪些字段是重复的也容易 代码如下: SELECT `name`,