写在前面

1、不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程

2、不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小

3、但把mysql客户端(如SQLyog,如HeidiSQL)放在桌面上,时不时拿出来 explain 一把,这是一种美德!

在实例讲解之前,我们先回顾一下联表查询的基础知识

联表查询的基础知识

引子:为什么第一个查询using temporary,第二个查询不用临时表呢?

下面两个查询,它们只差了一个order by,效果却迥然不同。

第一个查询:

EXPLAIN extended
SELECT ads.id FROM ads, city
WHERE
city.city_id = 8005
AND ads.status = 'online'
AND city.ads_id=ads.id
ORDER BY ads.id desc

执行计划为:

id  select_type table  type    possible_keys  key          key_len  ref             rows  filtered  Extra
1
SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using temporary; Using filesort
1
SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where

第二个查询:

EXPLAIN extended
SELECT ads.id FROM ads,city
WHERE
city.city_id =8005
AND ads.status = 'online'
AND city.ads_id=ads.id
ORDER BY city.ads_id desc

执行计划里没有了using temporary:

id  select_type table type    possible_keys  key           key_len  ref              rows  filtered  Extra
1
SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using where; Using filesort
1
SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where

为什么?DBA告诉我们:

MySQL 表关联的算法是 Nest Loop Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。

EXPLAIN 结果中,第一行出现的表就是驱动表(Important!)

以上两个查询语句,驱动表都是 city,如上面的执行计划所示!

对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序(Important!)

因此,order by ads.id desc 时,就要先 using temporary 了!

驱动表的定义

wwh999 在 2006年总结说,当进行多表连接查询时, [驱动表] 的定义为:

1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表]

2)未指定联接条件时,行数少的表为[驱动表](Important!)

忠告:如果你搞不清楚该让谁做驱动表、谁 join 谁,请让 MySQL 运行时自行判断

既然“未指定联接条件时,行数少的表为[驱动表]”了,而且你也对自己写出的复杂的 Nested Loop Join 不太有把握(如下面的实例所示),就别指定谁 left/right join 谁了,请交给 MySQL优化器 运行时决定吧。

如果您对自己特别有信心,可以像火丁一样做优化

小结果集驱动大结果集

de.cel 在2012年总结说,不管是你,还是 MySQL,优化的目标是尽可能减少JOIN中Nested Loop的循环次数。

以此保证:永远用小结果集驱动大结果集(Important!)!

实例讲解

Nested Loop Join慢查SQL语句

先了解一下 mb 表有 千万级记录,mbei 表要少得多。慢查实例如下:

explain
SELECT mb.id, ……
FROMmb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mbei.apply_time DESC
limit 0,10

够复杂吧。Nested Loop Join 就是这样,以驱动表的结果集作为循环的基础数据,然后将结果集中的数据作为过滤条件一条条地到下一个表中查询数据,最后合并结果。

此时还有第三个表,则将前两个表的 Join 结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此反复。

这条语句的执行计划如下:

id select_type table   type    possible_keys   key           key_len  ref       rows       Extra
1
SIMPLE mb index userid userid 4 (NULL) 6060455 Using index; Using temporary; Using filesort
1
SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1
1
SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

由于动用了“LEFT JOIN”,所以攻城狮已经指定了驱动表,虽然这张驱动表的结果集记录数达到百万级!

如何优化?

优化第一步:LEFT JOIN改为JOIN,干嘛要 left join 啊?直接 join!

explain
SELECT mb.id……
FROM mb JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mbei.apply_time DESC
limit 0,10

立竿见影,驱动表立刻变为小表 mbei 了, Using temporary 消失了,影响行数少多了:

id select_type table  type     possible_keys     key         key_len  ref                rows    Extra
1
SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13383 Using filesort
1
SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1
SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

优化第一步之分支1:根据驱动表的字段排序,好吗?

left join不变。干嘛要根据非驱动表的字段排序呢?我们前面说过“对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序!”的。

explain
SELECT mb.id……
FROM mb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mb.id DESC
limit 0,10

也满足业务场景,做到了rows最小:

id select_type table  type     possible_keys  key           key_len  ref        rows  Extra

1 SIMPLE mb index userid PRIMARY 4 (NULL) 10

1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 Using index

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

优化第二步:去除所有JOIN,让MySQL自行决定!写这么多密密麻麻的 left join/inner join 很开心吗?

explain

SELECT mb.id……

FROM mb,mbei,u

WHERE

mb.id=mbei.mb_id

and mb.uid=u.user_id

order by mbei.apply_time desc

limit 0,10

立竿见影,驱动表一样是小表 mbei:

id select_type table   type    possible_keys      key          key_len  ref                rows    Extra

1 SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13388 Using filesort

1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

最后的总结:

强调再强调:

1、不要过于相信你的运气!

2、不要相信你的开发环境里SQL的执行速度!

3、请拿起 explain 武器,如果你看到以下现象,请优化:

1)出现了Using temporary

2)rows过多,或者几乎是全表的记录数

3)key 是 (NULL)

4)possible_keys 出现过多(待选)索引

记住,explain 是一种美德!

【explain】MySQL联表查询中的驱动表的更多相关文章

  1. 了解MySQL联表查询中的驱动表,优化查询,以小表驱动大表

    一.为什么要用小表驱动大表 1.驱动表的定义 当进行多表连接查询时, [驱动表] 的定义为: 1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表] 2)未指定联接条件时,行数少的表为[驱动表 ...

  2. [慢查优化]联表查询注意谁是驱动表 & 你搞不清楚谁join谁更好时请放手让mysql自行判定

    写在前面的话: 不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程: 不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小: ...

  3. 【转】[慢查优化]联表查询注意谁是驱动表 & 你搞不清楚谁join谁更好时请放手让mysql自行判定

    转自:http://zhengyun-ustc.iteye.com/blog/1942797 写在前面的话: 不要求每个人一定理解 联表查询(join/left join/inner join等)时的 ...

  4. MySQL数据库之单表查询中关键字的执行顺序

    目录 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 2 执行顺序 3 关键字使用语法 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 select distinct from ...

  5. MySQL多表查询之外键、表连接、子查询、索引

    MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...

  6. 分析比较多表查询中的IN与JOIN

    IN 是子查询的关键字,JOIN 是连接的关键字,项目开发中经常会使用到多表查询,而子查询与连接正是实现多表查询的重要途径.那两者是怎么运行的?IN与JOIN哪个更好?下面就来分析与比较. 现在有te ...

  7. Mysql Join-连接查询(中)

    Mysql Join-连接查询(中) 认识 就我平时的数据接触来看, 连接查询也没有很复杂,不够是非常需要耐心和逻辑的, 一点点将数据查出来, 拼接等. 没有什么技巧, 多练习就会了. 无非就是表之间 ...

  8. 将一个多表关联的条件查询中的多表通过 create select 转化成一张单表的sql、改为会话级别临时表 【我】

    将一个多表关联的条件查询中的多表通过 create   select  转化成一张单表的sql 将结果改为创建一个会话级别的临时表: -- 根据下面这两个sql CREATE TABLE revenu ...

  9. 【杂记】mysql 左右连接查询中的NULL的数据筛选问题,查询NULL设置默认值,DATE_FORMAT函数

    MySQL左右连接查询中的NULL的数据筛选问题 xpression 为 Null,则 IsNull 将返回 True:否则 IsNull 将返回 False. 如果 expression 由多个变量 ...

随机推荐

  1. UVAlive 6560 - The Urge to Merge(状压dp)

    LA 6560 - The Urge to Merge option=com_onlinejudge&Itemid=8&page=show_problem&problem=45 ...

  2. Java类集综合练习——信息管理(增、删、改、查)

    一.实现功能——模拟学生选课功能 1.选择课程 2.修改所选课程 二.实现功能——模拟学生信息管理功能 1.添加学生信息 2.修改学生信息 二.主要代码(在同一个包里) 1.课程类 public cl ...

  3. <LeetCode OJ> 326. Power of Three

    326. Power of Three Question Total Accepted: 1159 Total Submissions: 3275 Difficulty: Easy 推断给定整数是否是 ...

  4. UVA1630 Folding 区间DP

    Folding Description   Bill is trying to compactly represent sequences of capital alphabetic characte ...

  5. word2vec (一) 简介与训练过程概要

    摘自:http://blog.csdn.net/thriving_fcl/article/details/51404655 词的向量化与word2vec简介 word2vec最初是Tomas Miko ...

  6. 【BZOJ 2982】 combination

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2982 [算法] lucas定理 [代码] #include<bits/stdc ...

  7. 1-1restful简介及资源介绍

  8. VS中的路径宏

    说明$(RemoteMachine)设置为“调试”属性页上“远程计算机”属性的值.有关更多信息,请参见更改用于 C/C++ 调试配置的项目设置.$(References)以分号分隔的引用列表被添加到项 ...

  9. QlikSense系列(3)——QlikSense建立数据模型

    QlikSense管理数据在帮助中写的比较清楚 https://help.qlik.com/zh-CN/sense/3.1/Subsystems/Hub/Content/LoadData/load-d ...

  10. hdu2282 Chocolate 完美匹配 + 拆点

    题意: N个箱子排成一个圈,所有的箱子里的巧克力的数量加起来不大于N,每次可以把箱子里的巧克力向旁边的箱子转移(两个方向),问要让每个箱子里的巧克力不大于1的最小步数. 分析: 把巧克力大于1的箱子拆 ...