表變量與臨時表

在SQL Server的性能調優中,有一個不可比面的問題:那就是如何在一段需要長時間的代碼或被頻繁調用的代碼中處理臨時數據集?表變量和臨時表是兩種選擇。

表變量

表變量是一種特殊的數據類型,是變量的一種,可用于存儲結果集以進行后續處理。表主要用于臨時存儲結果集返回的行。如果聲明函數和變量的類型為表,則表變量可在函數、存儲的過程和批處理。

創建表變量語法

DECLARE @<表變量名> 
  Table( { <column_definition> | <table_constraint> } [ ,...n ] )
<column_definition> ::=   
    column_name scalar_data_type   
    [ COLLATE <collation_definition> ]   
    [ [ DEFAULT constant_expression ] | IDENTITY [ ( seed , increment ) ] ]   
    [ ROWGUIDCOL ]   
    [ column_constraint ] [ ...n ]   
  
<column_constraint> ::=   
    { [ NULL | NOT NULL ]   
    | [ PRIMARY KEY | UNIQUE ]   
    | CHECK ( logical_expression )   
    }   
  
<table_constraint> ::=   
     { { PRIMARY KEY | UNIQUE } ( column_name [ ,...n ] )  
     | CHECK ( logical_expression )   
     }   

表變量一般備注

  • 表可以像一般的 FROM 子句中按名稱引用變量,如下面的示例所示︰
SELECT Employee_ID, Department_ID FROM @MyTableVar; 
  • 若有關聯查詢,則在FROM 子句,外部表必須使用一個別名,來引用變量,如下面的示例中所示︰
SELECT EmployeeID, DepartmentID   
FROM @MyTableVar m  
JOIN Employee on (m.EmployeeID =Employee.EmployeeID AND  
   m.DepartmentID = Employee.DepartmentID);  
  • 表變量行為類似于本地變量。 有明確定義的作用域。 這就是在其中聲明該變量的函數、存儲過程或批處理。在其范圍內,表可像常規表中使用變量。 該變量可應用于 SELECT、INSERT、UPDATE 和 DELETE 語句中用到表或表的表達式的任何地方。在定義表變量的函數、存儲過程或批處理結束時,會自動清除表變量;
  • 表變量,使用系統內存,讀寫速度快,但內存是有一定限制,所有操作無日志。
  • 表變量是不需要考慮其他會話訪問的問題,因此也不需要鎖機制,對于非常繁忙的系統來說,避免鎖的使用可以減少一部分系統負載;
  • 表變量并不是都存在于內存中,表變量存放在內存是有一定限制的,如果表變量數據量超過閾值,會把內存耗盡,然后使用TempDB的空間。
  • 由于表變量不會寫日志,不會造成鎖開銷,不能在Declare之外創建主鍵索引等,因此表變量不會造成架構的變化,從而不會造成重編譯。該存儲過程的執行計劃已經在創建存儲過程的時候生成了,因此之后執行的存儲過程使用表變量不會造成執行計劃的重編譯。

表變量的限制:

  • 不能對表變量執行SELECT INTO語句,如:
SELECT select_list INTO table_variable;
  • 在SQL Server2000中,表變量也不能用于INSERT INTO table_variable EXEC stored_procedure這樣的語句中,而在2005之后的版本就開始支持這個用法了。
  • 在DECLARE后,不能再對表變量進行更改,即無法進行DDL操作;
  • 不能直接在表變量上創建索引(因為一旦你創建一個表變量之后,就不能對其進行DDL語句了,這包括Create Index語句),但可以通過創建約束(主鍵、唯一)來建立索引;
  • 表變量聲明中的檢查約束、默認值以及計算所得的列不能調用用戶定義的函數。
  • 表變量不支持變量之間的賦值操作;
  • 因為表變量具有有限的范圍,并不是持久的數據庫的一部分,它們不受事務回滾;
  • 表變量存在于內存,當大數據量時,使用表變量的話就太耗內存了;
  • 在表變量上不能創建非聚集索引(為 PRIMARY 或 UNIQUE 約束創建的系統索引除外)。與具有非聚集索引的臨時表相比,這可能會影響查詢性能;
  • 表變量不具有數據分布的統計信息,它們不會觸發重新編譯。在許多情況下,優化器會在假定 table 變量沒有行的前提下生成查詢計劃。這樣不利于優化器做出正確的執行計劃,不適合數據量較大的情況。
  • 如果表變量是在 EXEC 語句或 sp_executesql 存儲過程外創建的,則不能使用 EXEC 語句或sp_executesql 存儲過程來運行引用該表變量的動態 SQL Server 查詢。由于表變量只能在它們的本地作用域中引用

【示例】
(1)表變量的創建與查詢

-- Create the table variable.  
DECLARE @MyTableVar table(  
    LocationID int NOT NULL,  
    CostRate smallmoney NOT NULL,  
    NewCostRate AS CostRate * 1.5,  
    ModifiedDate datetime);  
  
-- Insert values into the table variable.  
INSERT INTO @MyTableVar (LocationID, CostRate, ModifiedDate)  
    SELECT LocationID, CostRate, GETDATE() FROM Production.Location  
    WHERE CostRate > 0;  
  
-- View the table variable result set.  
SELECT * FROM @MyTableVar;  
GO  

(2)在INSERT..EXEC中使用表變量

--獲取借書記錄分析情況
create proc sp_GetBorrowAnalysis
    @BeginTime    datetime,
    @EndTime    datetime 
as
    --建一個表變量,結構與sp_GetBorrowRecord查詢出的結果集相同
     declare @Record table 
     (
      BookID int,                --書籍ID
      BookName varchar(100),    --書籍名稱
      TypeID int,                --書籍類別ID
      CardID int                --借書卡ID
      CardName varchar(100)        --借書人姓名
     )

    --獲取這段時間內的借書記錄,并存入@Record表變量中
    insert into @Record exec sp_GetBorrowRecord @BeginTime,@EndTime

臨時表

臨時表是臨時對象的一種,還有例如臨時存儲過程、臨時函數之類的臨時對象,臨時對象都存儲在tempdb中。

創建臨時表格式的兩種方式
①CREATE TABLE

CREATE TABLE  #|##
    [ database_name . [ schema_name ] . | schema_name . ] table_name   
    ( { <column_definition> } [ ,...n ] )   
  • 臨時對象都以#或##為前綴,以#前綴的臨時表為本地的,因此只有在當前用戶會話中才可以訪問,而##前綴的臨時表是全局的,因此所有用戶會話都可以訪問;
  • 創建臨時表的方法和創建普通表一樣,除了有以下不同:
  • 多了#|##前綴;
  • 當創建本地或全局臨時表時,CREATE TABLE 語法支持除 FOREIGN KEY 約束以外的其他所有約束定義;
  • 如果臨時表中指定了 FOREIGN KEY 約束,則該語句將返回一條表明已跳過此約束的警告消息。 此表仍將創建,但不使用 FOREIGN KEY 約束。 在 FOREIGN KEY 約束中不能引用臨時表;

②SELECT INTO
??使用SELECT INTO會自動生成臨時表,不需要事先創建

SELECT <select_list>
INTO  #|##<臨時表名>
FROM <table_source>
WHERE <search_condition>

臨時表一般備注

  • 臨時表的用法(如INSERT、UPDATE、DELETE)和一般的表一樣;
  • 臨時表不能分區;
  • 如果在單個存儲過程或批處理中創建了多個臨時表,則它們必須有不同的名稱;
  • 如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除,先truncate table,然后drop table,這樣可以避免系統表的較長時間鎖定;
  • 如果本地臨時表由存儲過程創建或由多個用戶同時執行的應用程序創建,則數據庫引擎必須能夠區分由不同用戶創建的表。 為此,數據庫引擎在內部為每個本地臨時表的表名追加一個數字后綴;
  • 臨時表以會話為邊界,除非使用 DROP TABLE 顯式刪除臨時表,否則臨時表將在退出其作用域時由系統自動刪除;

局部臨時表會在下列情況下被Drop:
a、顯式調用Drop Table語句;
b、當存儲過程完成時,將自動刪除在存儲過程中創建的本地臨時表。
c、當前會話結束,在會話內創建的所有局部臨時表都會被Drop;
全局臨時表會在下列情況下被Drop:
a、全局臨時表在創建此表的會話結束其他所有任務停止對其引用時將被自動刪除。 換言之,當創建全局臨時表的會話結束時,最后一條引用此表的 Transact-SQL 語句完成后,將自動刪除此表。

  • 臨時表存儲在TempDb中,因此臨時表的訪問是有可能造成物理IO的,當然在修改時也需要生成日志來確保一致性,同時鎖機制也是不可缺少的;

臨時表的約束

  • 不能對臨時表進行分區;
  • 不能對臨時表加外鍵約束;
  • 臨時表內列的數據類型不能定義成沒有在TempDb中沒有定義自定義數據類型(自定義數據類型是數據庫級別的對象,而臨時表屬于TempDb)

同名臨時表
??從一般備注我們知道,如果本地臨時表由存儲過程創建,數據庫引擎在內部為每個本地臨時表的表名追加一個數字后綴。當存儲過程完成時,將自動除去在存儲過程中創建的本地臨時表。所以即使在存儲過程或觸發器中創建的本地臨時表的名稱可以與在調用存儲過程或觸發器之前創建的臨時表名稱相同,但是臨時表在會話中只是一個代號,在實際的系統臨時庫中,真實表名會自動處理,所以它們也是不同的。
??在存儲過程或觸發器中創建的本地臨時表的名稱可以與在調用存儲過程或觸發器之前創建的臨時表名稱相同。 但是,如果查詢引用臨時表,而同時有兩個同名的臨時表,則不定義針對哪個表解析該查詢。
??嵌套存儲過程同樣可以創建與調用它的存儲過程所創建的臨時表同名的臨時表,套存儲過程中對表名的所有引用都被解釋為是針對該嵌套過程所創建的表。但是,為了對其進行修改以解析為在嵌套過程中創建的表,此表必須與調用過程創建的表具有相同的結構和列名。

CREATE PROCEDURE dbo.Test2  
AS  
    CREATE TABLE #t(x INT PRIMARY KEY);  
    INSERT INTO #t VALUES (2);  
    SELECT Test2Col = x FROM #t;  
GO  
  
CREATE PROCEDURE dbo.Test1  
AS  
    CREATE TABLE #t(x INT PRIMARY KEY);  
    INSERT INTO #t VALUES (1);  
    SELECT Test1Col = x FROM #t;  
EXEC Test2;  
GO  
  
CREATE TABLE #t(x INT PRIMARY KEY);  
INSERT INTO #t VALUES (99);  
GO  
  
EXEC Test1;  
GO  

下面是結果集:

(1 row(s) affected)
Test1Col
-----------
1
(1 row(s) affected)
Test2Col
-----------
2

【示例】

drop table #Tmp  --刪除臨時表#Tmp
create table #Tmp --創建臨時表#Tmp
(
  ID  int IDENTITY (1,1)   not null, --創建列ID,并且每次新增一條記錄就會加1
  WokNo        varchar(50),  
  primary key (ID)   --定義ID為臨時表#Tmp的主鍵   
);
Select * from #Tmp  --查詢臨時表的數據
truncate table #Tmp --清空臨時表的所有數據和約束

表變量與臨時表的對比

  • 臨時表是利用了硬盤(tempdb數據庫) ,表名變量是占用內存。在數據量比較大的時候,如果使用表變量,會把內存耗盡,然后使用TEMPDB的空間,這樣主要還是使用硬盤空間,但同時把內存基本耗盡,增加了內存調入調出的機會,反而降低速度。所以數據量比較少的時候可以使用表變量,數據量大時一般推薦使用臨時表。
  • 表變量缺省放在內存,速度快,因此建議觸發器、自定義函數用表變量;存儲過程看情況,大部分用表變量;特殊的應用,大數據量的場合用臨時表。
  • 無表關聯操作,只作為中間集進行數據處理,建議用表變量;有表關聯,且不能確定數據量大小的情況下,建議用臨時表。
  • 表變量需要事先知道表結構,普通臨時表,只在當前會話中可用與表變量相同into一下就可以了,方便。
  • 全局臨時表的功能是表變量沒法達到的。全局臨時表可在多個會話中使用。
  • 表變量不必刪除,也就不會有命名沖突,臨時表特別是全局臨時表用的時候必須解決命名沖突。
  • 在存儲過程中使用表變量與使用臨時表相比,減少了存儲過程的重新編譯量。
  • 事務支持:臨時表:支持,表變量:不支持
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,738評論 18 399
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,339評論 11 349
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,836評論 18 139
  • 我總想,從來沒有正兒八經的有過任何一段戀愛關系的人,才是真正的失戀者,可能全稱應該是“缺失愛戀關系的人”。a...
    一筐西紅柿閱讀 306評論 0 1
  • 大概是從很早了吧,電子游戲就已經逐漸走進我的生活,從單機的《仙劍奇俠傳》到后來的《CS》,《魔獸爭霸》,再到...
    再見徹羅基閱讀 218評論 0 0