2022-05-13 11:31:18 版本 : MySQL中的两种临时表
作者: 周杨 于 2022年05月13日 发布在分类 / 计算机应用 / 数据库 / MySql 下,并于 2022年05月13日 编辑
 历史版本

备注 修改日期 修改人
CREAT 2022-05-13 11:31:18[当前版本] 系统管理员

MySQL临时表在我们需要保存⼀些临时数据时是⾮常有⽤的。临时表只在当前连接可见,当关闭连接时,MySQL会⾃动删除表并释放所有空间。

使⽤其他MySQL客户端程序连接MySQL数据库服务器来创建临时表,那么只有在关闭客户端程序时才会销毁临时表,当然也可以⼿动删除。

⼀、MySQL中的两种临时表

外部临时表

通过CREATETEMPORARYTABLE创建的临时表,这种临时表称为外部临时表。这种临时表只对当前⽤户可见,当前会话结束的时候,该临时表会⾃动关闭。这种临时表的命名与⾮临时表可以同名(同名后⾮临时表将对当前会话不可见,直到临时表被删除)。

内部临时表

内部临时表是⼀种特殊轻量级的临时表,⽤来进⾏性能优化。这种临时表会被MySQL⾃动创建并⽤来存储某些操作的中间结果。 这些操作可能包括在优化阶段或者执⾏阶段。这种内部表对⽤户来说是不可见的,但是通过EXPLAIN或者SHOWSTATUS可以查看MYSQL是否使⽤了内部临时表⽤来帮助完成某个操作。内部临时表在SQL语句的优化过程中扮演着⾮常重要的⾓⾊,MySQL中的很多操作都要依赖于内部临时表来进⾏优化。但是使⽤内部临时表需要创建表以及中间数据的存取代价,所以⽤户在写SQL语句的时候应该尽量的去避免使⽤临时表。

内部临时表有两种类型:⼀种是HEAP临时表,这种临时表的所有数据都会存在内存中,对于这种表的操作不需要IO操作。另⼀种是OnDisk临时表,顾名思义,这种临时表会将数据存储在磁盘上。OnDisk临时表⽤来处理中间结果⽐较⼤的操作。如果HEAP临时表存储的 数据⼤于MAX_HEAP_TABLE_SIZE(详情请参考MySQL⼿册中系统变量部分),HEAP临时表将会被⾃动转换成OnDisk临时表。OnDisk临时表在5.7中可以通过INTERNAL_TMP_DISK_STORAGE_ENGINE系统变量选择使⽤MyISAM引擎或者InnoDB引擎。

⼆、外部临时表的常见⽤法

外部临时表是通过CREATE TEMPORARY TABLE及DROP TABLE来操作的,但是SHOW TABLES命令显⽰数据表列表时,你将⽆法看到

⾃⼰创建的临时表的。并且在退出当前会话后,临时表就会被⾃动销毁。当然也可以⼿动(DROP TABLE)销毁。

2.1、引擎类型:只能是:memory(heap)、myisam、merge、innodb,不⽀持mysql cluster(簇)。

2.2、外部临时表使⽤时注意⼏点:

1、⾃⼰所⽤的数据库账号要有建⽴临时表的权限;

2、在同⼀条sql中,不能关联2次相同的临时表,不然,就会报如下错误;

mysql>select*fromtemp_table,temp_tableast2;error1137:can'treopentable:'temp_table'

3、临时表在建⽴连接时可见,关闭时会清除空间,删除临时表;

4、show tables不会列出临时表;

5、不能使⽤rename重命名临时表。但是,你可以alter table代替:只能使⽤alter table old_tp_table_name rename new_tp_table_name; 

6、影响使⽤replication功能;

7、如果你为⼀个表声明了别名,当你指向这个表的时候,就必须使⽤这个别名。

⽰例:

mysql>CREATE TEMPORARY TABLESalesSummary (

->product_nameVARCHAR(50)NOTNULL

->, total_salesDECIMAL(12,2)NOTNULL DEFAULT0.00

->,avg_unit_priceDECIMAL(7,2)NOTNULLDEFAULT0.00

->,total_units_soldINTUNSIGNEDNOTNULLDEFAULT0

);
Query OK,0rows affected (0.00sec)
mysql>INSERT INTOSalesSummary
->(product_name, total_sales, avg_unit_price, total_units_sold)
->VALUES
->('cucumber',100.25,90,2);
mysql>SELECT*FROMSalesSummary;
+--------------+-------------+----------------+------------------+
|product_name|total_sales|avg_unit_price|total_units_sold|
+--------------+-------------+----------------+------------------+
|cucumber|100.25|90.00|2|
+--------------+-------------+----------------+------------------+
1rowinset(0.00sec)
mysql>DROP TABLESalesSummary; mysql>SELECT*FROMSalesSummary;
ERROR1146:Table'RUNOOB.SalesSummary'doesn't exist
(update id="createTempTable">
CREATE TEMPORARY TABLE IF NOT EXISTS temp
SELECT * FROM settlement_temp
WHERE settle_date=#{settleDate} ANDLENGTH(operator)IN(16,32) AND pay_status IN('01','06')ORDER BY settle_date,merchant_no
(/update>

2.3、mybatis中临时表操作

(!-- 4、删除临时表-->

(update id="dropTempTable">
DROP TEMPORARY TABLE IF EXISTS settlement_temp;
(/update>

三、内部临时表的常见⽤法

如果⽤户在书写SQL语句的时候能够尽量少的使⽤内部临时表进⾏查询优化,将有效的提⾼查询执⾏的效率。
CREATE TABLEt1( aint, bint);INSERT INTOt1VALUES(1,2),(3,4);
下⾯所有的操作都是基于表t1进⾏举例的。
例如:
mysql>explainformat=jsonselectSQL_BUFFER_RESULT*fromt1; EXPLAIN
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "2.00"
},
"buffer_result": {"using_temporary_table": true,"table": {
"table_name": "t1","access_type": "ALL",
...

mysql>explainformat=jsonselect*from(select*fromt1)astt;EXPLAIN
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "2.40"
},
"table": {"table_name": "tt",
"access_type": "ALL",
...
"materialized_from_subquery": { "using_temporary_table": true,
...
如果我们查询系统表的话,系统表的数据将被存储到内部临时表中。
例如:
mysql>select*frominformation_schema.character_sets;mysql>show statuslike'CREATE%';

mysql>explainformat=jsonselectdistinctafromt1; EXPLAIN
{
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "1.60"
},
"duplicates_removal": {"using_temporary_table": true,
...
如果查询带有ORDER BY语句,并且不能被优化掉。下⾯⼏种情况会利⽤到内部临时表缓存中间数据,然后对中间数据进⾏排序。
1)如果连接表使⽤BNL(BatchedNestloop)/BKA(BatchedKeyAccess)
mysql>explainformat=jsonselect*fromt1,t1ast2orderbyt1.a; EXPLAIN
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "22.00"
},
"ordering_operation": {"using_temporary_table": true,
...


mysql>setoptimizer_switch='block_nested_loop=off';Query OK,0rows affected (0.00sec)
mysql>explainformat=jsonselect*fromt1,t1ast2orderbyt1.a; EXPLAIN
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "25.00"
},
"ordering_operation": { "using_filesort": true,
...
2)ORDERBY的列不属于执⾏计划中第⼀个连接表的列。例如:
mysql>explainformat=jsonselect*fromtast1,tast2orderbyt2.a; EXPLAIN
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "25.00"
},
"ordering_operation": {"using_temporary_table": true,
...


3)如果ORDERBY的表达式是个复杂表达式。
例如:
drop function ifexistsfunc1;delimiter|
create functionfunc1(xint)returns intdeterministicbegin
declarez1, z2int;setz1=x;

⾸先我们定义⼀个表t1,

在SQL语句中使⽤SQL_BUFFER_RESULT hint

SQL_BUFFER_RESULT主要⽤来让MySQL尽早的释放表上的锁。因为如果数据量很⼤的话,需要较长时间将数据发送到客户端,通过将数据缓冲到临时表中可以有效的减少读锁对表的占⽤时间。SQL_BUFFER_RESULT见《》

如果SQL语句中包含了DERIVED_TABLE。

在5.7中,由于采⽤了新的优化⽅式,我们需要使⽤set optimizer_switch=’derived_merge=off’来禁⽌derived table合并到外层的Query中。例如:

我们当前不能使⽤EXPLAIN来查看是否读取系统表数据需要利⽤到内部临时表,但是可以通过SHOWSTATUS来查看是否利⽤到了内部临时表。

如果DISTINCT语句没有被优化掉,即DISTINCT语句被优化转换为GROUP BY操作或者利⽤UNIQUE INDEX消除DISTINCT,内部临时表将会被使⽤。

例如:

1))BNL默认是打开的

2))关掉BNL后,ORDER BY将直接使⽤filesort。

那么什么样的ORDER BY表达式,MySQL认为是复杂表达式呢?1))如果排序表达式是SP或者UDF。

setz2=z1+2;returnz2;
end|delimiter;
explain format=jsonselect*fromt1order byfunc1(a);
{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "2.20"
},
"ordering_operation": {"using_temporary_table": true,
...



create indexidx1ont1(a);
explain format=jsonSELECtaFROMt1group byaorder bysum(a);
|{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "1.20"
},
"ordering_operation": {"using_temporary_table": true,"using_filesort": true, "grouping_operation":{
"using_filesort":false,
...
drop indexidx1ont1;


例如:
explain format=jsonselect(selectrand()fromt1 limit1)asafromt1order bya;
|{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "1.20"
},
"ordering_operation": {"using_temporary_table": true,"using_filesort": true,
...


例如:
setsql_mode='';
create indexidx1ont1(b);
explain format=jsonselectt1.afromt1group byt1.border by1;
|{
"query_block": {"select_id":1,"cost_info": {
"query_cost": "1.40"
},
"ordering_operation": {"using_temporary_table": true,"using_filesort": true, "grouping_operation":{
"using_filesort":false,
...
drop indexidx1ont1;



explain format=jsonselectt2.afromt1, t1ast2group byt1.a;
|{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "8.20"
},
"grouping_operation": {"using_temporary_table": true,"using_filesort": true, "cost_info": {
"sort_cost": "4.00"
...


%1)如果GROUPBY的列不属于执⾏计划中的第⼀个连接表。例如:
explain format=jsonselectt2.afromt1, t1ast2group byt2.a;
|{
"query_block": {"select_id":1, "cost_info": {
"query_cost": "8.20"
},
"grouping_operation": {"using_temporary_table": true,"using_filesort": true, "nested_loop":[
...


%1)如果GROUPBY语句使⽤的列与ORDERBY语句使⽤的列不同。例如:
set sql_mode='';
explain format=json select t1.a from t1 group by t1.b order by t1.a;
| {
"query_block": {"select_id": 1, "cost_info": {
"query_cost": "1.40"
},
"ordering_operation": { "using_filesort": true, "grouping_operation": {"using_temporary_table": true,"using_filesort": false,
...
%1)如果GROUPBY带有ROLLUP并且是基于多表外连接。例如:
explain format=json select sum(t1.a) from t1 left join t1 as t2 on true group by t1.a with rollup;
| {"query_block":{"select_id": 1, "cost_info":{
"query_cost": "7.20"
},
"grouping_operation": {"using_temporary_table": true,"using_filesort": true, "cost_info": {
"sort_cost": "4.00"
},
...
%1)如果GROUPBY语句使⽤的列来⾃于SCALARSUBQUERY,并且没有被优化掉。例如:
explain format=json select (select avg(a) from t1) as a from t1 group by a;
| {"query_block":{"select_id": 1, "cost_info":{
"query_cost": "3.40"
},
"grouping_operation": {"using_temporary_table": true,"using_filesort": true, "cost_info": {
"sort_cost": "2.00"
},
...
1)如果semi-join执⾏⽅式为Materialization
例如:
set optimizer_switch='firstmatch=off,duplicateweedout=off'; explainformat=jsonselect*fromt1whereain(selectbfromt1);
| {"query_block":{"select_id": 1, "cost_info":{
"query_cost": "5.60"
},
"nested_loop": [
{
"rows_examined_per_scan": 1,"materialized_from_subquery": { "using_temporary_table": true,"query_block": {
"table": { "table_name": "t1","access_type": "ALL",
...
2)如果semi-join执⾏⽅式为DuplicateWeedout
例如:
set optimizer_switch='firstmatch=off';
explain format=json select * from t1 where a in (select b from t1);
| {"query_block": {"select_id": 1, "cost_info": {
"query_cost": "4.80"
},
"duplicates_removal": {"using_temporary_table": true,"nested_loop": [
{
...

explain format=json select * from t1 union select * from t1;
| {"query_block":{"union_result":{
"using_temporary_table": true,"table_name": "(union1,2>",
...
如果查询语句使⽤多表更新。
update t1, t1 as t2 set t1.a=3; show status like 'CREATE%';
如果聚集函数中包含如下函数,内部临时表也会被利⽤。
1)count(distinct*)
explain format=json select count(distinct a) from t1;
2)group_concat
explain format=json select group_concat(b) from t1;
总之,上⾯列出了10种情况,MySQL将利⽤内部临时表进⾏中间结果缓存,如果数据量⽐较⼤的话,内部临时表将会把数据存储在磁盘上,这样显然会对性能有所影响。为了尽可能的减少性能损失,我们需要尽量避免上述情况的出现。
当然了,如果临时表中需要存储的数据量超过了上限( 或 中取其⼤者),这时候就需要⽣成基于磁盘的临时表了。在以下⼏种情况下,会创建磁盘临时表:
见下例:
mysql> set default_tmp_storage_engine = "InnoDB";
-rw-rw---- 1 mysql mysql 8558 Jul 7 15:22 #sql4b0e_10_0.frm -- InnoDB引擎的临时表
-rw-rw----1 mysql mysql98304Jul 7 15:22#sql4b0e_10_0.ibd
-rw-rw----1 mysql mysql8558Jul 7 15:25#sql4b0e_10_2.frm
mysql> set default_tmp_storage_engine = "MyISAM";
-rw-rw----1mysqlmysql0Jul715:25#sql4b0e_10_2.MYD --MyISAM引擎的临时表
-rw-rw---- 1 mysql mysql 1024 Jul 7 15:25 #sql4b0e_10_2.MYI
mysql> set default_tmp_storage_engine = "MEMORY";
-rw-rw---- 1 mysql mysql 8558 Jul 7 15:26 #sql4b0e_10_3.frm -- MEMORY引擎的临时表

2))ORDER BY的列包含聚集函数

为了简化执⾏计划,我们利⽤INDEX来优化GROUP BY语句。例如:

3))ORDER BY的列中包含有SCALAR SUBQUERY,当然该SCALAR SUBQUERY没有被优化掉。

4)如果查询既带有ORDER BY同时也有GROUP BY语句,但是两个语句使⽤的列不相同。注意:如果是5.7,我们需要将sql_mode设置为⾮only_full_group_by模式,否则会报错。同样为了简化执⾏计划,我们利⽤INDEX来优化GROUP BY语句。

如果查询带有GROUPBY语句,并且不能被优化掉。下⾯⼏种情况会利⽤到内部临时表缓存中间数据,然后对中间数据进⾏GROUPBY。

1)如果连接表使⽤BNL(Batched Nestloop)/BKA(Batched Key Access)。例如:

IN表达式转换为semi-join进⾏优化

如果查询语句带有UNION,MySQL将利⽤内部临时表帮助UNION操作消除重复。例如:

这⾥Explain不能看到内部临时表被利⽤,所以需要查看status。例如:

例如:

例如:

MySQL在以下⼏种情况会创建临时表:

1、UNION查询;

2、⽤到TEMPTABLE算法或者是UNION查询中的视图;

3、ORDER BY和GROUP BY的⼦句不⼀样时;

4、表连接中,ORDER BY的列不是驱动表中的;

5、DISTINCT查询并且加上ORDER BY时;

6、SQL中⽤到SQL_SMALL_RESULT选项时;

7、FROM中的⼦查询;

8、⼦查询或者semi-join时创建的表;

EXPLAIN查看执⾏计划结果的Extra列中,如果包含 就表⽰会⽤到临时表。

1、数据表中包含BLOB/TEXT列;

2、在GROUP BY或者DSTINCT的列中有超过512字符 的字符类型列(或者超过512字节的 ⼆进制类型列,在5.6.15之前只管是否超过512字节);

3、在SELECT、UNION、UNION ALL查询中,存在最⼤长度超过512的列(对于字符串类型是512个字符,对于⼆进制类型则是512字节);

4、执⾏SHOW COLUMNS/FIELDS、DESCRIBE等SQL命令,因为它们的执⾏结果⽤到了BLOB列类型。

从5.7.5开始,新增⼀个系统选项 可定义磁盘临时表的引擎类型为InnoDB,⽽在这以前,只能使⽤MyISAM。⽽在5.6.3以后新增的系统选项 是控制CREATE TEMPORARY TABLE创建的临时表的引擎类型,在以前默认是MEMORY,不要把这⼆者混淆了。

历史版本-目录  [回到顶端]
    知识分享平台 -V 5.1.4 -大信谛威