看MySQL官方文档的示例SQL有感
【背景】
周末比较闲,我这个人又没有什么爱好,当然了读书除外;前一些天我一个同事说:“你一个dba想去写一本“django”书,合适吗?”
我想也是,一个人不能忘了本,所以MySQL还是要好好的搞一搞的;自那时起决定每周至少要写一篇MySQL博客,技术这东西不进则退
刚刚看官方文档的时候吓到了,本以为官方写的SQL应该是比较好的,是同一类问题的最佳实践,没想到并不是这样的;
The Row Holding the Maximum of a Certain Column 这一章节我就觉得有问题
【问题介绍】
假设我们建了一个用于保存商品信息的表,那我们如何查出最贵的商品是哪件呢?
drop table if exists shop; create table if not exists shop (
article int(4) UNSIGNED ZEROFILL default '' not null,
dealer char(20) default '' not null,
price double(16,2) default '0.00' not null,
primary key(article, dealer),
INDEX shop__price(price)); insert into shop values
(1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),
(3,'C',1.69),(3,'D',1.25),(4,'D',19.95); select * from shop;
-- +---------+--------+-------+
-- | article | dealer | price |
-- +---------+--------+-------+
-- | 0003 | D | 1.25 |
-- | 0003 | B | 1.45 |
-- | 0003 | C | 1.69 |
-- | 0001 | A | 3.45 |
-- | 0001 | B | 3.99 |
-- | 0002 | A | 10.99 |
-- | 0004 | D | 19.95 |
-- +---------+--------+-------+
-- 7 rows in set (0.00 sec)
1): 官方给出的查询语句如下
mysql> select a.*
from shop as a
where price = (select max(b.price) from shop as b);
+---------+--------+-------+
| article | dealer | price |
+---------+--------+-------+
| 0004 | D | 19.95 |
+---------+--------+-------+
可以看到官方给出的解决方案也是“直抒胸臆”,但是MySQL的物理查询并不是这么做的!
【官方的解决方案有什么问题】
一句话“性能问题”! 就上面的问题逻辑上要分两步走 1): 在shop表中找出最高的价格是多少 2): 在shop表中找出“价格”等于“最高价格”的行。
就这么简单的逻辑MySQL不会搞错吧!事实上MySQL还真会搞错这个逻辑,它是这样干的“循环表中的每一行,针对每一行都去比较一个当前
行的price是否等于 shop表的最高价格(每次循环都会去计算shop表的最高价格)”这样就引入了大量的没有必要的工作量,这样的SQL自然就慢
啦。
执行计划如下,可以说是铁证如山了
explain select a.*
-> from shop as a
-> where price = (select max(b.price) from shop as b);
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+------------------------------+
| 1 | PRIMARY | a | NULL | ref | shop__price | shop__price | 8 | const | 1 | 100.00 | Using where; Using index |
| 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+------------------------------+
2 rows in set, 1 warning (0.00 sec)
【面对这样的问题我们怎么办】
事实上这个问题的根源就是MySQL对“子查询”的支持不太好,那我们人为的把子查询分离出来就行了吧!是的就是这么干(just do IT)
select max(price) into @maxprice from shop;
select article,dealer,price from shop where price=@maxprice; -- +---------+--------+-------+
-- | article | dealer | price |
-- +---------+--------+-------+
-- | 0004 | D | 19.95 |
-- +---------+--------+-------+
-- 1 row in set (0.00 sec)
两次SQL对应的执行计划如下
explain select max(price) into @maxprice from shop;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
1 row in set, 1 warning (0.00 sec) explain select article,dealer,price from shop where price=@maxprice;
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | shop | NULL | ref | shop__price | shop__price | 8 | const | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
【一个dba要做点什么】
我上面的例子是在mysql-8.0.12中复现的,从侧面看出整个MySQL对“子查询”的处理都不太好,那还能说什么呢! 当然是不能在MySQL中使用
“子查询”啦! 要用别的方式重写SQL,可以用“join” 也可以用刚才的“变量” 等方式来处理。一句话“在MySQL中子查询不准上生产环境”
【学习交流】
-----------------------------http://www.sqlpy.com-------------------------------------------------


-----------------------------http://www.sqlpy.com-------------------------------------------------
看MySQL官方文档的示例SQL有感的更多相关文章
- 手把手教你看MySQL官方文档
前言: 在学习和使用MySQL的过程中,难免会遇到各种问题.不知道当你遇到相关问题时会怎么做,我在工作或写文章的过程中,遇到不懂或需要求证的问题时通常会去查阅官方文档.慢慢的,阅读文档也有了一些经验, ...
- MySQL8.0.28安装教程全程参考MySQL官方文档
前言 为了MySQL8.0.28安装教程我竟然在MySQL官方文档逛了一天,至此献给想入门MySQL8.0的初学者.以目前最新版本的MySQL8.0.28为示例进行安装与初步使用的详细讲解,面向初学者 ...
- 使用MySQL Yum存储库的快速指南【mysql官方文档】
使用MySQL Yum存储库的快速指南 抽象 MySQL Yum存储库提供用于在Linux平台上安装MySQL服务器,客户端和其他组件的RPM包.这些软件包还可以升级和替换从Linux发行版本机软件存 ...
- MySQL锁和事务(一):InnoDB锁(MySQL 官方文档粗翻)
// 写在前面,实际上,数据库加锁的类型和范围受到多种因素的影响,例如数据库隔离等级,SQL语句,是否使用主键.索引等等.可以查看博文: http://www.cnblogs.com/zhaoyl/p ...
- MySQL 官方文档
MySQL 5.6 Reference Manual Preface and Legal Notices 1 General Information 2 Installing and Upgradin ...
- MySQL官方文档
http://dev.mysql.com/doc/refman/5.7/en/index.html 没有比这更好的MySQL文档了,省的去买书了
- Mysql官方文档中争对安全添加列的处理方法。Mysql Add a Column to a table if not exists
Add a Column to a table if not exists MySQL allows you to create a table if it does not exist, but d ...
- MySQL 标识符到底区分大小写么——官方文档告诉你
最近在阿里云服务器上部署一个自己写的小 demo 时遇到一点问题,查看 Tomcat 日志后定位到问题出现在与数据库服务器交互的地方,执行 SQL 语句时会返回 指定列.指定名 不存在的错误.多方查证 ...
- Adaptive AUTOSAR 学习笔记 2 - 官方文档下载及阅读建议
目前互联网上没有太多的 Adaptive AUTOSAR 的学习资料,官方文档是一个很不错的途径.看过官方文档才发现,目前很多关于 Adaptive AUTOSAR 的文章都是官方文档的简化翻译,不如 ...
随机推荐
- web ide
https://www.jianshu.com/p/339dff3da1fa https://www.eclipse.org/che/ https://github.com/Coding/WebIDE ...
- Linux文件权限与目录
1:文件操作者的身份 owner:创建文件.拥有文件的登录用户. group:同一群组内的用户. others:其他登录用户. [系统账户与密码信息保存在/etc/passwd:个人账户与密码信息保存 ...
- Android ——VideoView禁止"无法播放该视频"弹窗
我们在使用videoView播放视频时,如果获取内容失败.网址不对.或者视频格式不对等,会弹出“无法播放该视频”的弹窗,阻塞用户使用. 这种情况,如果在一些自助服务类场合下,弹窗会造成十分不友好的用户 ...
- Java并发容器之非阻塞队列ConcurrentLinkedQueue
参考资料:http://blog.csdn.net/chenchaofuck1/article/details/51660521 实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,阻塞队列就是 ...
- nginx内置预定义变量
nginx的配置文件中可以使用的内置变量以美元符$开始,也有人叫全局变量.其中,部分预定义的变量的值是可以改变的. $arg_PARAMETER 这个变量值为:GET请求中变量名PARAMETER参数 ...
- phoneGap+jquery mobile项目经验
最近一个月,一直在用phoneGap+jquery mobile来开发一项目. 下面谈谈自己在开发过程中遇到的一些问题以及解决方法. 开始选择框架时,曾试过采用其他框架做UI,例如chocol ...
- Android中如何让DialogFragment全屏(DialogFragment fullscreen)
这个问题很让人纠结,因为我要在popopwindows里面使用fragment,但是在popopwindows里面找不到它的父控件,于是转战使用DialogFragment,但是让它全屏是一件很头疼的 ...
- Node.js相关——CommonJS规范
1. CommonJS规范产生背景 在后端,JavaScript的规范远远落后并且有很多缺陷,这使得难以使用JavaScript开发大型应用.比如: 没有模块系统 标准库较少 没有标准接口 缺乏包管理 ...
- Sketch Measure
Sketch Measure 让创建规范成为开发者和团队协作的乐趣 http://sketch.im/plugins/1 安装 下载安装包 双击 Sketch Measure.sketchplugin ...
- 【struts2】名为dispatcher的ResultType
1)基本使用 名称为“dispatcher”的ResultType,在struts-default.xml里的配置如下: <result-type name="dispatcher&q ...