日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

临时表与表变量深入探究

發布時間:2024/9/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 临时表与表变量深入探究 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

臨時表或表變量我們一般用來充當中間結果集,很多時候都在用,但真正了解他們之間的區別的人還是很少的,網上流傳的說法也不甚統一,所以今天我就做一個實驗,讓我們看看臨時表和表變量的區別,以及他們各自的用途。

執行以下語句,對測試環境做準備

DBCC DROPCLEANBUFFERS --從緩沖池中刪除所有清除緩沖區 DBCC FREEPROCCACHE --清除計劃緩存 CHECKPOINT --寫入MDF中

1) 關于存儲

表變量在內存中,是否真的不寫磁盤,不會造成任何IO開銷?

use tempdb exec sp_spaceused--database_name database_size unallocated space --tempdb 8.50 MB 6.75 MB--tempdb數據庫占用8.50M,未用空間6.75Muse TestDBCREATE TABLE #Table3(id int, AtypeId char(1024))declare @count int = 50 INSERT INTO #Table3(id, atypeid) SELECT TOP(@count) 1 as id, 'sss' FROM GraspFZDRPWrite001.dbo.BillType --隨便寫的一張表,只是讓其能循環插入50條記錄use tempdb CHECKPOINT --寫入MDF中exec sp_spaceused--database_name database_size unallocated space --tempdb 8.50 MB 6.62 MB

運行代碼,我們發現,unallocated space 未用空間減小了,從6.75M減少至6.62M,說明臨時表是占用了tempdb空間的,這點毋庸置疑。

我們接著看表變量又是如何?

use tempdb exec sp_spaceused--database_name database_size unallocated space --tempdb 8.50 MB 6.69 MBuse TestDBdeclare @Table3 table (id int, atypeid char(1024)) declare @count int = 50INSERT INTO @Table3(id, atypeid) SELECT TOP(@count) 1 as id, 'sss' as atypeid FROM GraspFZDRPWrite001.dbo.BillType use tempdb checkpointexec sp_spaceused --database_name database_size unallocated space --tempdb 8.50 MB 6.62 MB

unallocated space值再次變小,說明此操作存在占用tempdb的數據庫空間。兩者其實都存儲在tempdb中,都占用tempdb的數據庫空間。

2)對表變量記錄的操作是否占用更少的LOG

我們首先看臨時表插入

if OBJECT_ID('tempdb..#T') is not null drop table #Tdeclare @b1 bigint, @b2 bigintCREATE TABLE #T (s char(128))SELECT @b1=num_of_bytes_written from sys.dm_io_virtual_file_stats(2, 2) declare @i int = 0 while @i<20000 BEGINinsert into #T select '臨時表:原值'set @i=@i+1 ENDuse tempdb checkpoint select @b2=num_of_bytes_written from sys.dm_io_virtual_file_stats(2, 2) select @b2-@b1 as 日志增量 --經測試,臨時表日志增量 4851712

然后是表變量插入

use TestDBdeclare @b1 bigint, @b2 bigint declare @V table (s char(128)) select @b1=num_of_bytes_written from sys.dm_io_virtual_file_stats(2, 2) declare @i int = 0 while @i<20000 begininsert into @V select '表變量:原值'set @i=@i+1 end use tempdb checkpoint select @b2=num_of_bytes_written from sys.dm_io_virtual_file_stats(2, 2) select @b2-@b1 as 日志增量 --經測試,表變量日志增量5007360

兩者日志記錄相差不多,表變量還比臨時表的日志寫入更多!

3)Lock上的不同表現

--臨時表 if OBJECT_ID('tempdb..#T') is not null drop table #Tcreate table #T (s varchar(128)) insert into #T select '臨時表:原值'execute sp_lock @@spid --查看當前用戶進程的會話 所在的鎖關系BEGIN TRANSACTIONupdate #T set s= '臨時表:被更新'execute sp_lock @@spid --發現增加了一個排他鎖 ROLLBACK TRANSACTIONexecute sp_lock @@spid --排他鎖被釋放GO--表變量 declare @V table (s char(128)) insert into @V select '表變量:原值'execute sp_lock @@spidBEGIN TRANSACTIONupdate @V set s='表變量:被更新'execute sp_lock @@spid ROLLBACK TRANSACTIONexecute sp_lock @@spid --并沒有在事務中加任何鎖

臨時表的更新會加鎖,表變量更新不會

4)事務處理中的不同

if OBJECT_ID('tempdb..#T') is not null drop table #Tcreate table #T (s varchar(128)) declare @T table (s varchar(128)) insert into #T select '臨時表:原值' insert into @T select '表變量:原值'BEGIN TRANSACTIONupdate #T set s='臨時表:被更新'update @T set s='表變量:被更新' ROLLBACK TRANSACTIONselect * from #T select * from @T

結果發現,臨時表得值被回滾,表變量并沒有回滾。可以得出,表變量不受事務影響。

5)UDF中的不同

--表變量 CREATE FUNCTION dbo.example1 ( ) RETURNS INT AS BEGIN DECLARE @t1 TABLE (i INT) INSERT @t1 VALUES(1) INSERT @t1 VALUES(2) UPDATE @t1 SET i = i + 5 DELETE @t1 WHERE i < 7 DECLARE @max INT SELECT @max = MAX(i) FROM @t1 RETURN @max END GO; --臨時表 CREATE FUNCTION dbo.example2 ( ) RETURNS INT AS BEGIN CREATE TABLE #t1 (i INT) INSERT #t1 VALUES(1) INSERT #t1 VALUES(2) UPDATE #t1 SET i = i + 5 DELETE #t1 WHERE i < 7 DECLARE @max INT SELECT @max = MAX(i) FROM #t1 RETURN @max END GO --物理表 CREATE FUNCTION dbo.example3 ( ) RETURNS INT AS BEGIN CREATE TABLE table1 ( id INT IDENTITY, name VARCHAR(32) ) INSERT table1(name) VALUES('aaron') RETURN SCOPE_IDENTITY() END

運行后,可以發現,函數內無法訪問臨時表,也無法創建實體表,同理也無法更新新增刪除實體表的記錄,但可以使用表變量來進行運算和操作

6) 性能對比

if OBJECT_ID('tempdb..#T') is not null drop table #T create table #T (s char(1024)) declare @i int = 0 while @i<100000 begininsert into #T select '臨時表:原值'set @i=@i+1 END SELECT * FROM #T --3秒左右 --3秒 godeclare @V table (s char(1024)) declare @i int = 0 while @i<100000 begininsert into @V select '表變量:原值'set @i=@i+1 END SELECT * FROM @V --3秒左右,和臨時表幾乎一樣

在插入性能上,兩者基本一致

7)關聯操作上,性能的不同表現

--構造數據 dbcc dropcleanbuffers; --從緩沖池中刪除所有清除緩沖區use TestDBif object_id('tempdb..#temp') is not null drop table #temp SELECT IDENTITY(INT, 1, 1) as _rowid, a.ppt, a.GoodsId INTO #temp FROM GraspFZDRPWrite001.dbo.Goods a --goods表是一個有記錄16049的數據表GO--用臨時表 SELECT * FROM #temp WHERE _rowid IN (SELECT max(_rowid) FROM #temp GROUP BY Ppt) --很快 --用表變量 DECLARE @PDTEMP TABLE (_rowid int, ppt CHAR(1), goodsid INT) INSERT INTO @PDTEMP SELECT * FROM #tempSELECT * FROM @PDTEMP WHERE _rowid IN (SELECT max(_rowid) FROM @PDTEMP GROUP BY ppt) go --相當慢,記錄幾乎出不來

為啥會出現以上的結果呢,我們跟蹤執行計劃可以發現,前者執行計劃選擇的哈希匹配,后者則是相當緩慢的嵌套循環。

分析原因,因為聚合操作會利用表的統計信息來聚合,表變量沒有統計信息,系統默認只能選擇嵌套循環,而這導致嚴重的慢查詢的主要原因

我們強制查詢使用hash join連接(哈希匹配)

DECLARE @PDTEMP TABLE (_rowid int, ppt CHAR(1), goodsid INT) INSERT INTO @PDTEMP SELECT * FROM #tempSELECT * FROM @PDTEMP WHERE _rowid IN (SELECT max(_rowid) FROM @PDTEMP GROUP BY ppt) option(hash join)

這時速度跟用臨時表一樣,但不推薦這樣使用,因為一旦這樣強制使用,SQLSERVER的自動優化則不會起作用

8)把存儲過程中返回的數據集插入到臨時表 用于保存存儲過程中返回的數據集

CREATE TABLE #sp_who3 ( SPID INT, Status VARCHAR(32) NULL ) gocreate procedure pWho ASselect 1 as spid, 'Tomas' as statusunion allselect 1 as spid, 'Viviy' as status goinsert #sp_who3 execute pWhoselect * from #sp_who3DECLARE @PDTEMP TABLE (SPID INT, Status VARCHAR(32) NULL) INSERT @PDTEMP EXEC pWho SELECT * FROM @PDTEMPGO

兩者都可以正常使用,但是有一點必須注意,對于2008以前的版本,表變量是不支持這樣操作的

9)是否可以動態的生成列

--臨時表 SELECT * INTO #TEMP_objects FROM sys.objects; --成功執行--表變量 SELECT * INTO @PDTEMP FROM sys.objects; --報錯

表變量不能生成動態列,因為表變量一旦創建,他的架構就是固定的,而臨時表可以更改架構甚至是索引

10)生命期是否相同

if OBJECT_ID('tempdb..#temp_foo') is not null drop table #temp_foo--臨時表 DECLARE @sql VARCHAR(8000) SET @sql = 'Create TABLE #temp_foo (a INT,b INT,c INT) Insert into #temp_foo values(1,1,1) ' EXEC(@sql) INSERT #temp_foo SELECT 1,2,3 SELECT * FROM #temp_foo GO--表變量 DECLARE @sql VARCHAR(8000) SET @sql = 'DECLARE @foo TABLE(a INT,b INT,c INT) Insert into @foo values(1,1,1)' EXEC(@sql) INSERT @foo SELECT 1,2,3 Go;

同時存在兩個不同的批處理,外面的批處理要調用里面的批處理聲明的表,臨時表和表變量均不能使用

--臨時表 DECLARE @sql VARCHAR(8000) Create TABLE #temp_foo (a INT,b INT,c INT) SET @sql = 'Insert into #temp_foo values(1,1,1)' EXEC(@sql) INSERT #temp_foo SELECT 1,2,3 SELECT * FROM #temp_foo GO --可以執行--表變量 DECLARE @sql VARCHAR(8000) DECLARE @foo TABLE(a INT,b INT,c INT) SET @sql = 'Insert into @foo values(1,1,1)' EXEC(@sql) INSERT @foo SELECT 1,2,3 SELECT * FROM @foo Go --執行到EXEC(@SQL)報錯,找不到@foo

臨時表在跨批處理中,里面的批處理可以調用到外面聲明的表,表變量卻不行(實體表只要是聲明在前,都可以被調用)

總結: 無表關聯操作,只作為中間集進行數據處理,建議用表變量;有表關聯,且不能確定數據量大小的情況下,建議用臨時表。

總結

以上是生活随笔為你收集整理的临时表与表变量深入探究的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。