热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysql嵌套查询和联表查询优化方法

本文描述了mysql嵌套查询和联表查询优化的方法,有些情况可以使用这种优化方法,而有些情况,这种方法就无能为力了

本文描述了mysql嵌套查询和联表查询优化的方法,有些情况可以使用这种优化方法,而有些情况,这种方法就无能为力了

嵌套查询糟糕的优化
在上面我提到过,不考虑特殊的情况,联表查询要比嵌套查询更有效。尽管两条查询表达的是同样的意思,尽管你的计划是告诉服务器要做什么,然后让它决定怎么做,但有时候你非得告诉它改怎么做。否则优化器可能会做傻事。我最近就碰到这样的情况。这几个表是三层分级关系:category, subcategory和item。有几千条记录在category表,几百条记录在subcategory表,以及几百万条在item表。你可以忽略category表了,我只是交代一下背景,以下查询语句都不涉及到它。这是创建表的语句:

[sql]
代码如下:
create table subcategory (
id int not null primary key,
category int not null,
index(category)
) engine=InnoDB;

create table item(
id int not null auto_increment primary key,
subcategory int not null,
index(subcategory)
) engine=InnoDB; 我又往表里面填入一些样本数据

[sql]
代码如下:
insert into subcategory(id, category)
select i, i/100 from number
where i <= 300000;

insert into item(subcategory)
select id
from (
select id, rand() * 20 as num_rows from subcategory
) as x
cross join number
where i <= num_rows;

create temporary table t as
select subcategory from item
group by subcategory
having count(*) = 19
limit 100;

insert into item (subcategory)
select subcategory
from t
cross join number
where i <2000; 再次说明,这些语句运行完需要一点时间,不适合放在产品环境中运行。思路是往item里插入随机行数的数据,这样subcategory就有1到2018之间个item。这不是实际中的完整数据,但效果一样。

我想找出某个category中item数大于2000的全部subcategory。首先,我找到一个subcategory item数大于2000的,然后把它的category用在接下来的查询中。这是具体的查询语句:

[sql]
代码如下:
select c.id
from subcategory as c
inner join item as i on i.subcategory = c.id
group by c.id
having count(*) > 2000;

-- choose one of the results, then
select * from subcategory where id = ????
-- result: category = 14 我拿到一个合适的值14,在以下的查询中会用到它。这是用来查询category 14 中所有item数大于2000的subcategory的语句:

[sql]
代码如下:
select c.id
from subcategory as c
inner join item as i on i.subcategory = c.id
where c.category = 14
group by c.id
having count(*) > 2000; 在我的样例数据里,查询的结果有10行记录,而且只用10多秒就完成了。EXPLAIN显示出很好地使用了索引;从数据的规模来看,相当不错了。查询计划是在索引上遍历并计算出目标记录。目前为止,非常好。

这回假设我要从subcategory取出全部的字段。我可以把上面的查询当成嵌套,然后用JOIN,或者SELECT MAX之类(既然分组集对应的值都是唯一的),但也写成跟下面的一样的,有木有?

[sql]
代码如下:
select * from subcategory
where id in (
select c.id
from subcategory as c
inner join item as i on i.subcategory = c.id
where c.category = 14
group by c.id
having count(*) > 2000
); 跑完这条查询估计要从破晓到夕阳沉入大地。我不知道它要跑多久,因为我没打算让它无休止地跑下去。你可能认为,单从语句上理解,它会:a)计算出里面的查询,找出那10个值,b)继续找出那10条记录,并且在primary索引上去找会非常地快。错,这是实际上的查询计划:

[sql]
代码如下:
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: subcategory
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 300783
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: c
type: ref
possible_keys: PRIMARY,category
key: category
key_len: 4
ref: const
rows: 100
Extra: Using where; Using index; Using temporary; Using filesort
*************************** 3. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: i
type: ref
possible_keys: subcategory
key: subcategory
key_len: 4
ref: c.id
rows: 28
Extra: Using index 如何你不熟悉如何分析mysql的语句查询计划,请看大概意思:mysql计划从外到内执行查询,而不是从内到外。我会一个一个地介绍查询的每个部分。

外面的查询简单地变成了SELECT * FROM subcategory。虽然里面的查询对subcategory有个约束(WHERE category = 14),但出于某些原因mysql没有将它作用于外面的查询。我不知道是神马原因。我只知道它扫描了整张表(这就是 type:ALL 表示的意思),并且没有使用任何的索引。这是在10几万行记录的表上扫描。

在外面的查询,对每行都执行一次里面的查询,尽管没有值被里面的查询使用到,因为里面的查询被“优化”成引用外面的查询。照此分析,查询计划变成了嵌套循环。外面的查询的每一次循环,都执行一次里面的查询。下面就是优化器重写后的查询计划:

[sql]
代码如下:
select * from subcategory as s
where (
s.id,(
select c.id
from subcategory as c
join item as i
where ((i.subcategory = c.id) and (c.category = 14))
group by c.id
having ((count(0) > 2000)
and ((s.id) = (c.id))))
) 你可以通过在EXPLAIN EXTENDED 后面带上SHOW WARNINGS 得到优化后的查询。请留意在HAVING子句中指向的外部域。

我举这个例子并非有意抨击mysql的优化策略。众所皆知mysql在有些情况下还不能很好地优化嵌套查询,这个问题已经被广泛报告过。我想指出的是,开发者有必要检查查询语句确保它们不是被糟糕地优化。大多数情况下,安全起见若非是非必要,避免使用嵌套——尤其是WHERE...IN() 和 WHERE...NOT IN语句。

我自己的原则是“有疑问,EXPLAIN看看”。如果面对的是一个大数据表,我会自然而然地产生疑问。

如何强制里面的查询先执行
上一节中的语句撞板只因为mysql把它当成相关的语句从外到里地执行,而不是当成不相关语句从里到外执行。让mysql先执行里面的查询也是有办法的,当成临时表来实现,从而避免巨大的性能开销。

mysql从临时表来实现嵌套查询(某种程度上被讹传的衍生表)。这意味着mysql先执行里面的查询,并且把结果储存在临时表中,然后在其他的表里用到它。这就是我写这个查询时所期待的执行方式。查询语句修改如下:
[sql]
代码如下:
select * from subcategory
where id in (
select id from (
select c.id
from subcategory as c
inner join item as i on i.subcategory = c.id
where c.category = 14
group by c.id
having count(*) > 2000
) as x
); 我所做的就是把嵌套包着原来的嵌套查询。mysql会认为最里面是一个独立的嵌套查询先执行,然后现在只剩下包着外面的嵌套,它已经被装进一个临时表里,只有少量记录,因此要快很多。依此分析,这是相当笨的优化办法;倒不如把它重写成join方式。再说,免得被别人看到,当成多余代码清理掉。

有些情况可以使用这种优化方法,比如mysql抛出错误,嵌套查询的表在其他地方被修改(译注:另一篇文章 MySQL SELECT同时UPDATE同一张表 )。不幸的是,对于临时表只能在查询语句中使用一次的情况,这种方法就无能为力了。

来源 http://blog.csdn.net/afeiqiang/article/details/8620038
推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 推荐一个ASP的内容管理框架(ASP Nuke)的优势和适用场景
    本文推荐了一个ASP的内容管理框架ASP Nuke,并介绍了其主要功能和特点。ASP Nuke支持文章新闻管理、投票、论坛等主要内容,并可以自定义模块。最新版本为0.8,虽然目前仍处于Alpha状态,但作者表示会继续更新完善。文章还分析了使用ASP的原因,包括ASP相对较小、易于部署和较简单等优势,适用于建立门户、网站的组织和小公司等场景。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
author-avatar
刺猬xiaojie
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有