MySQL 派生表(Derived Table) Merge Optimization

本文将通过演示告诉你:MySQL中派生表(Derived Table)是什么?以及MySQL对它的优化。

Background

有如下一张表:

mysql> desc city;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| country    | varchar(40) | YES  |     | NULL    |       |
| population | int(11)     | YES  |     | NULL    |       |
| city       | varchar(40) | YES  |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+

例如,如果首先考虑选择人口超过10,000人的城市,然后选择那些位于德国的城市,那么可以写这个SQL:

SELECT *
FROM
(SELECT * FROM city WHERE population > 10*1000) AS big_city
WHERE
big_city.country=‘Germany‘;

使用 EXPLAIN 命令查看执行计划:

mysql> EXPLAIN SELECT * FROM (SELECT * FROM city WHERE population > 1*1000) AS big_city WHERE big_city.country=‘Germany‘ ;
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
|  1 | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL | 4068 | Using where |
|  2 | DERIVED     | City       | ALL  | Population    | NULL | NULL    | NULL | 4079 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
2 rows in set (0.60 sec)

注意:mysql 5.7 需要设置 derived_merge=off,才会有上面的结果。否则MySQL会把临时表合并到外层查询,具体可参见我的另一篇文章《MySQL中的两种临时表》。

MySQL 的做法是:

经历如下3个步骤:

  1. 执行子查询:(SELECT * FROM city WHERE population > 1*1000),正如查询语句中的那样;
  2. 把子查询的结果写到临时表 big_city ;
  3. 回读,应用上层SELECT的WHERE条件 big_city.country=‘Germany‘ 。

执行这样的子查询是非常低效的,因为扫描基表 city 时没有使用父选择(country =‘Germany‘)的高选择性条件。 我们从City表中读取太多记录,然后我们必须将它们写入一个临时表并再次读取,然后才能过滤掉它们。

Derived table merge in action

如果在MariaDB / MySQL 5.6中运行此查询,则可以得到以下结果:

MariaDB [world]> EXPLAIN SELECT * FROM (SELECT * FROM City WHERE Population > 1*1000) AS big_city WHERE big_city.Country=‘Germany‘;
+----+-------------+-------+------+--------------------+---------+---------+-------+------+------------------------------------+
| id | select_type | table | type | possible_keys      | key     | key_len | ref   | rows | Extra                              |
+----+-------------+-------+------+--------------------+---------+---------+-------+------+------------------------------------+
|  1 | SIMPLE      | City  | ref  | Population,Country | Country | 3       | const |   90 | Using index condition; Using where |
+----+-------------+-------+------+--------------------+---------+---------+-------+------+------------------------------------+
1 row in set (0.00 sec)

从上面的结果可以看出:

  1. 只有一行输出,说明子查询已经被合并到上级的 SELECT 语句;
  2. 通过Country列访问City表,Country=‘Germany‘ 用来构建表上的 ref 访问;
  3. 查询将读取大约90行,这是对于之前的4079行读加上4068行临时表读/写的一个很大的改进。

Factsheet

派生表(FROM子句中的子查询)可以在没有 grouping, aggregates, or ORDER BY ...  LIMIT 子句时合并到他们的父查询中。这个优化默认开启,可通过如下关闭:

set @@optimizer_switch=‘derived_merge=OFF‘

不支持该优化的Maria和MySQL版本将执行子查询,这可以导致一个著名的Bug(see e.g. MySQL Bug #44802),从MariaDB 5.3+和MySQL 5.6+ 开始,EXPLAIN命令立即执行,无论 derived_merge 如何设置。

Reference:

Derived Table Merge Optimization

时间: 2024-10-05 23:38:59

MySQL 派生表(Derived Table) Merge Optimization的相关文章

Mysql碰到“Every derived table must have its own alias”类似错误说明

Every derived table must have its own alias 这句话的意思是说每个派生出来的表都必须有一个自己的别名 一般在多表查询时,会出现此错误. 因为,进行嵌套查询的时候子查询出来的的结果是作为一个派生表来进行上一级的查询的,所以子查询的结果必须要有一个别名 把MySQL语句改成:select count(*) from (select * from --) as total; 问题就解决了,虽然只加了一个没有任何作用的别名total,但这个别名是必须的 sele

Mysql 分表查询引擎MERGE

http://www.eduyo.com/doc/mysql/storage-engines.html#merge-storage-engine 如果你DROP MERGE表,你仅在移除MERGE规格.底层表没有受影响.

修改MYSQL数据库表的字符集

MySQL 乱码的根源是的 MySQL 字符集设置不当的问题,本文汇总了有关查看 MySQL 字符集的命令.包括查看 MySQL 数据库服务器字符集.查看 MySQL 数据库字符集,以及数据表和字段的字符集.当前安装的 MySQL 所支持的字符集等. 一.查看 MySQL 数据库服务器和数据库字符集. mysql> show variables like '%char%';+--------------------------+----------------------------------

MYSQL优化派生表(子查询)在From语句中的

Mysql 在5.6.3中,优化器更有效率地处理派生表(在from语句中的子查询): 优化器推迟物化子查询在from语句中的子查询,知道子查询的内容在查询正真执行需要时,才开始物化.这一举措提高了性能: 1:之前版本(5.6.3),from语句中的子查询在explain select 查看执行计划语句执行时就会物化.它导致了部分查询执行,但explain语句的目的是获取执行计划信息,而不是执行查询 该版本物化不会在explain中发生,所以explain执行计划结果的得到更快: 2:因为上面提及

MySQL 子查询(三) 派生表、子查询错误

From MySQL 5.7 ref:13.2.10.8 Derived Tables 八.派生表 派生表是一个表达式,用于在一个查询的FROM子句的范围内生成表. 例如,在一个SELECT查询的FROM子句中的子查询,就是一个派生表. SELECT ... FROM (subquery) [AS] tbl_name ... [AS] tbl_name子句是必需的,因为FROM子句中的每个表都必须具有名称.且派生表中的任何列都必须具有唯一名称. 为了便于说明,假设现在有这样一个表: CREATE

使用Merge存储引擎实现MySQL分表

一.使用场景 Merge表有点类似于视图.使用Merge存储引擎实现MySQL分表,这种方法比较适合那些没有事先考虑分表,随着数据的增多,已经出现了数据查询慢的情况. 这个时候如果要把已有的大数据量表分开比较痛苦,最痛苦的事就是改代码.所以使用Merge存储引擎实现MySQL分表可以避免改代码. Merge引擎下每一张表只有一个MRG文件.MRG里面存放着分表的关系,以及插入数据的方式.它就像是一个外壳,或者是连接池,数据存放在分表里面. 对于增删改查,直接操作总表即可. 二.建表 1.用户1表

mysql出现错误“ Every derived table must have its own alias”

Every derived table must have its own alias 这句话的意思是说每个派生出来的表都必须有一个自己的别名 一般在多表查询时,会出现此错误. 因为,进行嵌套查询的时候子查询出来的的结果是作为一个派生表来进行上一级的查询的,所以子查询的结果必须要有一个别名 问题就解决了,虽然只加了一个没有任何作用的别名total,但这个别名是必须的

mysql为表添加外键完成性约束 报错Can&#39;t create table &#39;sfkbbs.#sql-513_25&#39; (errno: 150)

代码 alter table sfk_son_module add constraint foreign key(father_module_id) references sfk_father_module(id) on delete restrict on update restrict; (constraint 后面可以加上约束名字) 错误原因是之前两张表的id的类型不一样,一个时int,一个时bigint 解决办法时修改表, alter table sfk_father_module mo

mysql 命令重命名表RENAME TABLE 句法

mysql 命令重命名表RENAME TABLE 句法 RENAME TABLE tbl_name TO new_tbl_name[, tbl_name2 TO new_tbl_name2,...]更名是以原子方式(atomically)执行,这就意味着,当更名正在运行时,其它的任何线程均不能该表.这使得以一个空表替换一个表成为可能. CREATE TABLE new_table (...);RENAME TABLE old_table TO backup_table, new_table TO