mysql中的临时表如何使用

目录
  • 1.什么是临时表
  • 2.临时表的使用场景
    • union
    • groupby
  • 3.groupby 如何优化
  • 总结

1.什么是临时表

内部临时表是sql语句执行过程中,用来存储中间结果的的数据表,其作用类似于:join语句执行过程中的joinbuffer,order by语句执行过程中的sortBuffer一样。

这个表是mysql自己创建出来的,对客户端程序不可见。那么mysql什么时候会创建内部临时表呢?创建的内部临时表的表结构又是怎么样的呢?

2.临时表的使用场景

在mysql中常见的使用临时表的场景,有两个:unoin语句和groupby语句。

为了更好的了解内部临时表在unoin和groupby中是如何起作用的,我们先了解一下unoin和groupby的执行流程。

为了方便下文的描述,我们建立如下表结构:

CREATE TABLE `t1` (
  `id` int(11) NOT NULL,
  `a` int(11)  DEFAULT NULL,
  `b` int(11) default null,
  PRIMARY KEY (`id`) USING BTREE,
  key (`a`) using BTREE
) ENGINE=InnoDB;

建立表t1,其中id为主键,a为普通索引,然后向表中插入1000条数据:

drop procedure idata;
delimiter ;;
create procedure idata()
begin
	declare i int;
	set i=1;
	while(i<=1000)do
		insert into `t1` values(i,i,i);
		set i=i+1;
	end while;
end;;
delimiter ;
call idata();

union

我们都知道union的语义是对 unoin两端的结果集取并集,也就是两个结果集加起来,重复的数据行,只取其中一行。这里需要注意,unoin是有在多个数据集中排重的语义的。

下面我们执行下面这条语句:

(select 1000 as f) union (select id from t1 order by id desc limit 2);

在这条语句的语义是:将t1中的数据,按照id倒序排列后,取出前两行数据的id,与"1000"取并集。

这条语句在mysql中的执行流程如下:

1.创建一个内存临时表,这个内存临时表只有一个整形字段f,并且f字段为主键(因为要进行排重)。

2.执行第一个子查询,得到1000这个值,放入到内存临时表中。

3.执行第二个子查询:取出第一个满足条件数据行中的id=1000,尝试写入临时表,这时会出现违反唯一性约束的情况,导致插入失败,然后继续执行。取出第二行数据的id=999,插入成功。

4.从临时表中取出数据,返回客户端结果,并删除临时表。

同时,我们可以查看上述查询语句的执行计划,来验证上述执行流程:

从以上过程中,我们可以知道,内存临时表的作用:通过唯一键约束,实现了union的语义。

如果把上述语句中的union,替换成 union all的话,那查询语句就失去了"去重"的语义了。那么,mysql在执行查询语句的过程中,是否还会使用临时表呢?

我们使用以下查询语句进行验证:

(select 1000 as f) union all (select id from t1 order by id desc limit 2);

通过查询sql的执行计划,我们会发现,查询语句执行过程中,不在需要临时表了。

整个查询语句的执行流程如下:

1.执行第一个子查询,将查询的结果,作为结果集的一部分,返回给客户端。

2.执行第二个子查询,将查询的结果作为结果集的一部分,返回给客户端。

groupby

除了unoin查询语句在执行过程中会使用临时表外,groupby 查询语句在执行过程中,也会使用临时表。为了方便说明问题,我们执行如下查询语句:

select id%10 as m, count(1) as c from t1 group by m order by m;

该语句的语义,将表中所有数据中的id值,对10进行取模,并将取模后的结果进行分组,然后统计出每组数据的个数。查询语句执行计划如下:

该查询语句的执行流程如下:

1.创建临时表,表中有两个字段:m和c,其中m为主键,因为group by字段m的值,必须是唯一的。

2.扫描表t1的索引a,依次取出叶子结点上的id值,并计算id%10,将计算结果记为x,如果临时表中没有m=x的行,就插入一个记录(x,1)。如果表中有m=x数据行,那么就将x这一行的c值加1。

3.遍历完成后,在根据字段m做排序,得到最终结果返回给客户端。

对于步骤3中的排序流程,可以参考 如何优化sql中的orderBy。

3.groupby 如何优化

通过上面的描述,我们知道了groupby的执行流程。groupby在执行过程中,需要建立一个带有唯一键索引的临时表,其中唯一键索引字段就是groupby的字段。这个执行代价还是比较高的,而且这个临时表还是一次性的。

为了提高groupby语句的执行性能,我们可以从"不使用临时表"的角度下手。首先我们可以这样想:要想让groupby的过程中不使用临时表,我们就要知道,临时表在groupby的过程中,解决了什么问题?如果,我们能找到另外一种不使用临时表,也能解决这个问题的方案,那么我们就可以不使用临时表了。

首先,我们知道,在日常开发过程中,我们使用groupby主要就是为了实现:将表中所有的数据,按照指定字段进行分组。把字段值相同的数据划分为一个组,然后在对组内的数据执行聚合函数,聚合函数计算的结果,作为结果集中的一行数据。

而在这个过程中,临时表的作用就是在扫描数据表的时候,对每行数据属于哪个组,进行记录,同时执行聚合函数的逻辑。之所以需要一个临时表来记录每行数据属于哪个组,主要是因为表中的数据,按照"group by字段"维度,不是有序的。

如果表中的数据本身就是按照"groupby字段"有序的话,也就是属于同一个组的数据都分布在一起,那么就不需要临时表,也可以对数据进行分组。 举例如下图,如果执行groupby,同时计算每组数据个数。执行流程大致如下:

1.从左到右扫描数据,并依次累加,当遇到第一个2时,说明已经积累了3个1了,此时结果集的第一行数据就是(1,3)。

2.当遇到第一个3的时候,说明已经积累了2个2了,此时结果集的第二行数据就是(2,2);

3.按照以上逻辑逐个计算,就可以得到最终结果。

在mysql中,如果分组字段上有索引的话,执行查询过程中,mysql就不会建立临时表了。

我们可以执行如下查询语句进行验证:

explain
select id as m from t1 group by id;

通过查看执行计划,我们可以发现,因为分组字段id,是主键,本身是有序的。这里并没有使用临时表:

但是很多时候,分组字段并不是表中的一个具体字段。而是通过一定计算后的逻辑字段,如:

select id%10 as m from t1 group by m

这里分组字段m,并不是t1表中的一个字段,而是对id对10取模后的一个逻辑字段。为了让分组字段有序,下面给大家介绍两种优化手段。

1.生成伴生字段,并建立索引

从mysql 5.7开始,支持了generated column机制,来实现字段数据的关联更新。如下语句:

alter table t1 add column z int generated always as (id % 10), add index (z);

为t1表增加字段z,z的字段值为id值与10取模后的结果,同时在z上添加索引。这样当我们再执行:

explain
select id%100 as m,count(*) as c from t1 group by m

或者:

explain
select z as m,count(id) as c from t1 group by m

执行计划如下:

此时就不在使用临时表了。

上面的伴生字段的方案,需要我们向表中添加额外字段,如果业务场景比较复杂,分组的场景比较多,使用伴生字段方案需要在表中增加的额外字段就会比较多。这将会使我们的数据表结果变得比较复杂。

2.直接对分组字段进行排序

如果我们可以预估到,在执行groupby语句时,分组后的数据量比较大,使用的内存临时表可能都无法存储,那么内存临时表就会被替换成磁盘临时表,这个替换的阈值,由变量"tmp_table_size"控制,该变量的默认值为16M,如果在查询语句执行过程,需要存放到临时表中的数据量超过16M,那么使用的临时表就会变成磁盘临时表,磁盘临时表默认的存储引擎是InnoDB,磁盘临时表的性能相比内存临时表性能更低。

对于这种情况,mysql提供了 SQL_BIG_RESULT语句,该语句的作用就是告诉优化器:这个语句涉及到的数据量比较大,直接使用磁盘临时表。但是这里使用的磁盘临时表,会调整存储的数据结构,数据结构不再是B+树,而是数组。

下面我们举例说明,执行如下查询语句的的流程如下:

explain
select sql_big_result id%100 as m,count(id) as c from t1 group by m ;

执行流程:

1.初始化sort_buffer,确定放入一个整形字段,记为m。

2.扫描t1索引a,依次取出叶子节点中的主键id的值,并对100取模,然后插入到sort_buffer中。

3.数据表扫描完后,对sort_buffer中的m进行排序。

4.排序后,就得到了一个针对分组字段的有序数组。

有了针对分组字段的有序数组,那么就可以通过遍历该数组实现groupby的语义了。

通过查看上述查询语句的执行计划,可以发现,不在使用临时表了。

总结

为了保证groupby的执行性能,在使用groupby的时候要做到以下几点:

1.尽量让 group by 过程用上表的索引,确认方法是 explain 结果里没有 Using temporary 和 Using filesort。

2.如果 group by 需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大 tmp_table_size 参数,来避免用到磁盘临时表。

3.如果数据量实在太大,使用 SQL_BIG_RESULT 这个提示,来告诉优化器直接使用排序算法得到 group by 的结果。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • MySQL临时表的使用方法详解

    目录 1. 写在前面的话 2. 临时表的使用 2.1 创建一个只存放亚洲国家信息的临时表 2.1.1 创建临时表 2.1.2 向临时表里写数据 2.2 在查询过程中直接创建临时表 2.3 查询临时表中的数据 2.4 删除临时表 3. 以上操作的全部代码 总结 1. 写在前面的话 在开发数据库时,特别是写存储过程,遇到比较复杂的需求,使用临时表可以简化很多逻辑.曾经在一家互联网金融公司供职,公司数据组团队做数据清洗,写SQL脚本时,一个查询语句可以套到数层查询,甚至十几层.看起来几百行上千行的脚本

  • MySQL使用临时表加速查询的方法

    本文实例讲述了MySQL使用临时表加速查询的方法.分享给大家供大家参考.具体分析如下: 使用MySQL临时表,有时是可以加速查询的,下面就为您详细介绍使用MySQL临时表加速查询的方法. 把表的一个子集进行排序并创建MySQL临时表,有时能加速查询.它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作.例如: 复制代码 代码如下: SELECT cust.name,rcVBles.balance,--other columns  SELECT cust.name,rcVBles.bala

  • 浅谈Mysql在什么情况下会使用内部临时表

    union执行 为了便于分析,使用一下sql来进行举例 CREATE TABLE t1 ( id INT PRIMARY KEY, a INT, b INT, INDEX ( a ) ); delimiter ;; CREATE PROCEDURE idata ( ) BEGIN DECLARE i INT; SET i = 1; WHILE ( i <= 1000 ) DO INSERT INTO t1 VALUES ( i, i, i ); SET i = i + 1; END WHILE;

  • Mysql 存储过程中使用游标循环读取临时表

    游标 游标(Cursor)是用于查看或者处理结果集中的数据的一种方法.游标提供了在结果集中一次一行或者多行前进或向后浏览数据的能力. 游标的使用方式 定义游标:Declare 游标名称 CURSOR for table;(table也可以是select出来的结果集) 打开游标:Open 游标名称; 从结果集获取数据到变量:fetch 游标名称 into field1,field2; 执行语句:执行需要处理数据的语句 关闭游标:Close 游标名称; BEGIN # 声明自定义变量 declare

  • 使用MySQL实现select into临时表的功能

    目录 MySQL select into临时表 select into 临时表 create 临时表 mysql临时表(可以将查询结果存在临时表中) 创建临时表可以将查询结果寄存 mysql把select结果保存为临时表,有2种方法 MySQL select into临时表 最近在编写sql语句时,遇到两次将数据放temp表,然后将两次的temp表进行inner join,再供后续insert数据时使用的场景. 写完后发现执行耗时较长,需要优化,于是将一条长长的sql语句拆分成一个sql脚本,用

  • MySQL中关于临时表的一些基本使用方法

    临时表可能是非常有用的,在某些情况下,保持临时数据.最重要的是应该知道的临时表是,他们将当前的客户端会话终止时被删除. 临时表中添加MySQL版本3.23.如果您使用的是旧版本的MySQL比3.23,可以不使用临时表,但可以使用堆表. 如前所述临时表将只持续只要的会话是存在的.如果运行一个PHP脚本中的代码,该临时表将被销毁时,会自动执行完脚本后.如果已连接到MySQL数据库的服务器上,通过MySQL的客户端程序的临时表将一直存在,直到关闭客户端或手动破坏的表. 实例 下面是一个例子,使用临时表

  • MySQL中临时表的基本创建与使用教程

    当工作在非常大的表上时,你可能偶尔需要运行很多查询获得一个大量数据的小的子集,不是对整个表运行这些查询,而是让MySQL每次找出所需的少数记录,将记录选择到一个临时表可能更快些,然后在这些表运行查询. 创建临时表很容易,给正常的CREATE TABLE语句加上TEMPORARY关键字: CREATE TEMPORARY TABLE tmp_table ( name VARCHAR(10) NOT NULL, value INTEGER NOT NULL ) 临时表将在你连接MySQL期间存在.当

  • MySQL中临时表的使用示例

    这两天事情稍微有点多,公众号也停止更新了几天,结果有读者催更了,也是,说明还是有人关注,利己及人,挺好. 今天分享的内容是MySQL中的临时表,对于临时表,之前我其实没有过多的研究,只是知道MySQL在某些特定场景下会使用临时表来辅助进行group by等一些列操作,今天就来认识下临时表吧. 1.首先.临时表是session级别的,当前session创建的表,在其他session中看不到. session 1: mysql> create temporary table test3 (id_tm

  • MySQL内部临时表的具体使用

    目录 UNION 表初始化 执行语句 UNION RESULT UNION ALL GROUP BY 内存充足 执行语句 执行过程 排序过程 ORDER BY NULL 内存不足 执行语句 优化方案 优化索引 直接排序 执行过程 对比DISTINCT 小结 参考资料 UNION UNION语义:取两个子查询结果的并集,重复的行只保留一行 表初始化 CREATE TABLE t1(id INT PRIMARY KEY, a INT, b INT, INDEX(a)); DELIMITER ;; C

  • mysql中的临时表如何使用

    目录 1.什么是临时表 2.临时表的使用场景 union groupby 3.groupby 如何优化 总结 1.什么是临时表 内部临时表是sql语句执行过程中,用来存储中间结果的的数据表,其作用类似于:join语句执行过程中的joinbuffer,order by语句执行过程中的sortBuffer一样. 这个表是mysql自己创建出来的,对客户端程序不可见.那么mysql什么时候会创建内部临时表呢?创建的内部临时表的表结构又是怎么样的呢? 2.临时表的使用场景 在mysql中常见的使用临时表

  • MySQL中查询字段为空或者为null的方法

    目录 MySQL查询字段为空或者为null 判断为null 判断为空或空格 MySQL查询字段为空(null)时设置默认值 总结 MySQL查询字段为空或者为null 判断为null select * from table where  column is null; 不为null: select * from table where  column is not null; 判断为空或空格 select * from table where column =''; 注:不管是空还是其中有空格都

  • MySQL中Update、select联用操作单表、多表,及视图与临时表的区别

    一.MySQL中使用从表A中取出数据来更新表B的内容 例如:要update表data中的一些列属性,但是修改属性的内容来源是来自表chanpin.SQL语言中不要显示的出现select关键字 update data d,chanpin c set d.zhulei=c.zhulei,d.xiaolei=c.xiaolei,d.fenxiang=c.fenxiang,d.zhuanye=c.zhuanye,d.jiliang=c.jiliang,d.gs=c.zgs,d.xzgs=c.zgs,d.

  • Mysql中explain作用详解

    一.MYSQL的索引 索引(Index):帮助Mysql高效获取数据的一种数据结构.用于提高查找效率,可以比作字典.可以简单理解为排好序的快速查找的数据结构. 索引的作用:便于查询和排序(所以添加索引会影响where 语句与 order by 排序语句). 在数据之外,数据库还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用数据.这样就可以在这些数据结构上实现高级查找算法.这些数据结构就是索引. 索引本身也很大,不可能全部存储在内存中,所以索引往往以索引文件的形式存储在磁盘上. 我们

  • MySQL中在查询结果集中得到记录行号的方法

    如果需要在查询语句返回的列中包含一列表示该条记录在整个结果集中的行号, ISO SQL:2003 标准提出的方法是提供 ROW_NUMBER() / RANK() 函数. Oracle 中可以使用标准方法(8i版本以上),也可以使用非标准的 ROWNUM : MS SQL Server 则在 2005 版本中提供了 ROW_NUMBER() 函数:但在 MySQL 中似乎还没有这样的系统自带功能.虽然 LIMIT 可以很方便的对返回的结果集数量和位置进行过滤,但过滤出来的记录的行号却没办法被 S

  • MySQL中distinct语句的基本原理及其与group by的比较

    DISTINCT 实际上和 GROUP BY 操作的实现非常相似,只不过是在 GROUP BY 之后的每组中只取出一条记录而已.所以,DISTINCT 的实现和 GROUP BY 的实现也基本差不多,没有太大的区别.同样可以通过松散索引扫描或者是紧凑索引扫描来实现,当然,在无法仅仅使用索引即能完成 DISTINCT 的时候,MySQL 只能通过临时表来完成.但是,和 GROUP BY 有一点差别的是,DISTINCT 并不需要进行排序.也就是说,在仅仅只是 DISTINCT 操作的 Query

  • MySQL两种临时表的用法详解

    外部临时表 通过CREATE TEMPORARY TABLE 创建的临时表,这种临时表称为外部临时表.这种临时表只对当前用户可见,当前会话结束的时候,该临时表会自动关闭.这种临时表的命名与非临时表可以同名(同名后非临时表将对当前会话不可见,直到临时表被删除). 内部临时表 内部临时表是一种特殊轻量级的临时表,用来进行性能优化.这种临时表会被MySQL自动创建并用来存储某些操作的中间结果.这些操作可能包括在优化阶段或者执行阶段.这种内部表对用户来说是不可见的,但是通过EXPLAIN或者SHOW S

  • MySQL中浮点型转字符型可能会遇的问题详解

    前言 本文主要给大家介绍了MySQL中在将浮点型转字符型的时候遇到的一个问题,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一 问题描述 今天遇到一个刷数据的需求,是修改产品的重量(字段类型为float),修改了产品的重量之后,是需要记录到日志表中的(字段类型为varchar),表结构如下: 临时刷数据表: CREATE TABLE `temp_170830` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主

随机推荐