备注 | 修改日期 | 修改人 |
CREAT | 2022-05-13 11:31:18[当前版本] | 系统管理员 |
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,不要把这⼆者混淆了。