临时表VS表变量:因地制宜,合理使用

借用火影忍者中宇智波. 鼬的一句名言:”任何术都是有缺陷的” 同样,在数据库的世界里没有哪项技术是完美无缺的.根据实际的场景,情形,选择合理的实现方式才是我们的初衷。 2014-09-25 09:43:29 临时表表变量SQL PostgreSQL祭起自己的NoSQL法宝 译文 以MongoDB以及CouchDB为代表的NoSQL数据库彼此之间一直斗得热火朝天、不亦乐乎,然而如今又有一款关系型数据库通过转型加入到NoSQL领域的竞争中来。 2014-09-16 10:24:49 PostgreSQLNoSQL 通过Redis实现RPC远程方法调用 我发现经常研究并且为之兴奋的一件事就是对系统进行扩展。现在这对不同的人有着不同的意思。作为移植Monolithic应用到Microservices架构方法中的一部分,如何处理Microservices架构是我研究RPC的原因。 2014-09-02 10:43:45 RedisRPC 商品信息数据分析和展现系统的设计与开发 商品价格和人们生活息息相关,比如农产品价格波动不仅会对农民收入和农民生产积极性产生直接影响,更关乎百姓的日常生活和切身利益。经常看新闻看到农民辛苦了一个季度的农作物全都烂在地里,因为价格太低廉,或者没有销路。虽然说物价波动是一种正常的经济现象,但是在一定程度上物价稳,人心才稳,社会才稳。为保持经济平稳健康发展、保障群众生活,稳定物价,信息公开显得尤为重要。价格监测和预测是维持物价稳定的一个重要环节。而对于商品价格、产量、以及销路、销量等数据公开,建立产品价格分析、监测、预测平台可以为相关政府管理 2014-09-01 09:09:05 商品信息数据分析 SQL Server数据库设计规范 数据库设计是指对一个给定的应用环境,构造最优的数据库模式,建立数据库及其他应用系统,使之能有效地存储数据,满足各种用户的需求。数据库设计过程中命名规范很是重要,命名规范合理的设计能够省去开发人员很多时间去区别数据库实体。

借用火影忍者中宇智波. 鼬的一句名言:”任何术都是有缺陷的” 同样,在数据库的世界里没有哪项技术是完美无缺的.根据实际的场景,情形,选择合理的实现方式才是我们的初衷。

一直以来大家对临时表表变量的孰优孰劣争论颇多,一些技术群里的朋友甚至认为表变量几乎一无是处,比如无统计信息,不支持事务等等.但事实并非如此.这里我就临时表与表变量做个对比,对于大多数人不理解或是有歧义的地方进行详细说明.

注:这里只讨论一般临时表,对全局临时表不做阐述.

生命周期

临时表:会话中,proc中,或使用显式drop

表变量:batch中

这里用简单的code说明表变量作用域

  1. DECLARE@tTABLE(iint)----定义表变量@t
  2. SELECT*FROM@t-----访问OK
  3. insertinto@tselect1-----插入数据OK
  4. select*from@t-------访问OK
  5. go-------结束批处理
  6. select*from@t-------不在作用域出错

注意:虽然说sqlserver在定义表变量完成前不允许你使用定义的变量.但注意下面情况仍然可正常运行!

  1. if'a'='b'
  2. begin
  3. DECLARE@tTABLE(iint)
  4. end
  5. SELECT*FROM@t-----仍然可以访问!

日志机制

临时表与表变量都会记录在tempdb中记录日志

不同的是临时表的活动日志在事务完成前是不能截断的.

这里应注意的是由于表变量不支持truncate,所以完全清空对象结果集时临时表有明显优势,而表变量只能delete

事务支持

临时表:支持

表变量:不支持

我们通过简单的实例加以说明

  1. createtable#t(iint)
  2. declare@ttable(iint)
  3. BEGINTRANttt
  4. insertinto#tselect1
  5. insertinto@tselect1
  6. SELECT*FROM#t------returns1rows
  7. SELECT*FROM@t------returns1rows
  8. ROLLBACKtranttt
  9. SELECT*FROM#t-------norows
  10. SELECT*FROM@t-------still1rows
  11. droptable#t----nousedrop@tinsession

锁机制(select)

临时表 会对相关对象加IS(意向共享)锁

表变量 会对相关对象加SCH-S(架构共享)锁(相当于加了nolock hint)

可以看出虽说锁的影响范围不同,但由于作用域都只是会话或是batch中,临时表的IS锁虽说兼容性不如表变量的SCH-S但绝大多数情况基本无影响.

感兴趣的朋友可以用TF1200测试

索引支持

临时表 支持

表变量 条件支持(仅SQL2014)

没错,在sql2014中你可以在创建表的同时创建索引 图1-1

注:在sql2014之前表变量只支持创建一个默认的唯一性约束

cod

  1. DECLARE@tTABLE
  2. (
  3. col1intindexinx_1CLUSTERED,
  4. col2intindexindex_2NONCLUSTERED,
  5. indexindex_3NONCLUSTERED(col1,col2)
  6. )

临时表VS表变量:因地制宜,合理使用

图1-1

  1. CREATEFUNCTIONTVP_Customers(@custnvarchar(10))
  2. RETURNSTABLE
  3. AS
  4. RETURN
  5. (SELECTRowNum,CustomerID,OrderDate,ShipCountry
  6. FROMBigOrders
  7. WHERECustomerID=@cust);
  8. GO
  9. CREATEFUNCTIONTVF_Customers(@custnvarchar(10))
  10. RETURNS@TTABLE(RowNumint,CustomerIDnchar(10),OrderDatedate,
  11. ShipCountrynvarchar(30))
  12. AS
  13. BEGIN
  14. INSERTINTO@T
  15. SELECTRowNum,CustomerID,OrderDate,ShipCountry
  16. FROMBigOrders
  17. WHERECustomerID=@cust
  18. RETURN
  19. END;
  20. DBCCFREEPROCCACHE
  21. GO
  22. SELECT*FROMTVF_Customers('CENTC');
  23. GO
  24. SELECT*FROMTVP_Customers('CENTC');
  25. GO
  26. SELECT*FROMTVF_Customers('SAVEA');
  27. GO
  28. SELECT*FROMTVP_Customers('SAVEA');
  29. GO
  30. selectb.text,a.execution_count,a.*fromsys.dm_exec_query_statsa
  31. crossapplysys.dm_exec_sql_text(a.sql_handle)b
  32. whereb.textlike'%_Customers%'

临时表VS表变量:因地制宜,合理使用

图1-2

其它方面

表变量不支持select into,alter,truncate,dbcc等

表变量不支持table hint 如(force seek)

执行计划预估

我想这里可能是引起使用何种方式争论比较突出的地方,由于表变量没有统计信息,无法添加索引等使得大家对其在执行计划中的性能表现嗤之以鼻,但实际情况呢?我们需要深入分析.

关于临时表的预估这里我就不做介绍了,主要对表变量的预估做详细阐述.

表变量在sql2000引入的一个原因就是为了在一些执行过程中减少重编译.以获得更好的性能.当然带来好处的同时也会带来一定弊端.由于其不涉及重编译,优化器其实并不知道表变量中的具体行数,此时他采取了保守的预估方式:预估行数为1行.如图2-1

Code

  1. declare@ttable(iint)
  2. select*from@t-----此时0行预估行数为1行
  3. insertinto@tselect1
  4. select*from@t-----此时1行,预估行数仍为1行
  5. insertinto@tvalues(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
  6. select*from@t----此时19行,预估行数仍为1行
  7. --....无论实际@t中有多少行,由于没有重编译,预估均为1行

临时表VS表变量:因地制宜,合理使用

图2-1

所以当我们加上重编译的的操作,此时优化器就知道了表变量的具体行数.如图2-2

Code

  1. declare@ttable(iint)
  2. select*from@toption(recompile)-----此时0行预估行数为1行
  3. insertinto@tselect1
  4. select*from@toption(recompile)-----此时1行,预估行数为1行
  5. insertinto@tvalues(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
  6. select*from@toption(recompile)----此时19行,预估行数为19行
  7. --....当加入重编译hint时,优化器就知道的表变量的行数.

临时表VS表变量:因地制宜,合理使用

图2-2

至此,我们可以看到优化器知道了表变量中的行数.这样在表变量扫描的过程中,尤其针对数据量较大的情形,不会因为预估总是1而引起一些问题.

如果你刚知道这里的预估原理,现有的代码都加上重编译那工作量可想而知了..这里介绍一个新的跟踪标记,Trace Flag 2453.

TF2453可以一定程度上替代重编译Hint,但只是在非简单计划(trivial plans)的情形下

注:TF2453只在sql2012 SP2和SQL2014中的补丁中起作用

#p#

表变量谓词预估

由于表变量木有统计信息,在优化器知道整体行数的前提下将会根据谓词的情形

采用不同的规则"猜"来进行预估.

注:这里有些规则笔者未找到微软相应的算法文档,经过自己根据数据推算得出.

看到这里的朋友请为我点个赞J(很长时间推算得出.可能数学忘得差不多了)

注:由于检索对象本身及为变量,谓词为变量,或是常数无影响

常见谓词下预估算法:

a ">", "<" 运算符 按照表变量数据量的30%进行预估

b "like" 运算符 按照表变量数据量的10%进行预估

c "=" 运算符 按照表变量数据量的0.75次方预估

实例如图2-3

code

  1. declare@iint
  2. set@i=13
  3. DECLARE@TTABLE(IINT);
  4. INSERTINTO@TVALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
  5. ------表变量中存在个数字
  6. select*from@TwhereI<1option(recompile)------20*30%预估数为6
  7. select*from@TwhereI>@ioption(recompile)--------20*30%预估数为6
  8. select*from@TwhereIlike@ioption(recompile)--------20*10%预估数为2
  9. select*from@TwhereIlike1option(recompile)--------20*10预估数为2
  10. select*from@TwhereI=@ioption(recompile)--------POWER(20.00000,0.75)预估数为9.45742
  11. select*from@TwhereI=1option(recompile)--------POWER(20.00000,0.75)预估数为9.45742
  12. insertinto@T
  13. selectDatabaseLogIDfromAdventureWorks2008R2.dbo.DatabaseLog------insertnewrecords
  14. select*from@Toption(recompile)------------此时数据为行
  15. select*from@TwhereI=1option(recompile)--------------------POWER(1617.00000,0.75)预估数为254.99550

临时表VS表变量:因地制宜,合理使用

图2-3

可以看出根据不同的谓词优化器会采用不同的预估方式,虽然它不如统计信息下的密度,直方图等来的精确(尤其是等值预估,在数据量巨大的情形下,其效果可能接近统计信息),但在了解数据的前提下如果适合表变量我们还是可以大胆使用的.

Tempdb竞争

tempdb的竞争本身涵盖的知识面比较大,这里我们只讨论临时表与表变量的孰优孰劣.

通过前面的介绍我们知道临时表是支持事务的,而表变量时不支持的.正因如此很多人放弃了表变量的使用.但任何事情都有两方面,支持就一定好吗?由于临时表对事务的支持,在高并发的情形中可能正因为其事务的支持造成系统表锁,总而影响并发.

我们通过一个简单的实例来说明

日常管理中,我发现很多开发人员在使用临时表时采用select * into #t from …的语法,这样的写法如果数据量稍大,将会造成事务持有系统表锁的时间变长,从而影响并发,吞吐.我们通过一个简单的实例说明.如图3-1

Code 我们通过sqlquerystress模拟并发

  1. ----SSMS测试数据
  2. Usetempdb
  3. createtablet
  4. (idintidentity,str1char(8000))----morepagesformanyrecords
  5. insertintotselect'a'
  6. go100
  7. ----sqlquerystress
  8. select*into#t
  9. fromt----57s
  10. ----sqlquerystress
  11. declare@ttable
  12. (idint,str1char(8000))
  13. insertinto@t
  14. select*fromt-----1s

临时表VS表变量:因地制宜,合理使用

图3-1

通过图3-1可以看出上述情形中临时表简直不堪重负.临时表与表变量到底该如何应用不是看谁比谁的优点多,应视具体情形而定

结语:借用火影忍者中宇智波. 鼬的一句名言:”任何术都是有缺陷的” 同样,在数据库的世界里没有哪项技术是完美无缺的.根据实际的场景,情形,选择合理的实现方式才是我们的初衷.

原文出自:http://www.cnblogs.com/shanksgao/p/3988089.html

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月8日 00:56
下一篇 2023年5月8日 00:56

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信