表變量與臨時表

在SQL Server的性能調(diào)優(yōu)中备蚓,有一個不可比面的問題:那就是如何在一段需要長時間的代碼或被頻繁調(diào)用的代碼中處理臨時數(shù)據(jù)集?表變量和臨時表是兩種選擇厚柳。

表變量

表變量是一種特殊的數(shù)據(jù)類型军熏,是變量的一種,可用于存儲結(jié)果集以進(jìn)行后續(xù)處理肿轨。表主要用于臨時存儲結(jié)果集返回的行。如果聲明函數(shù)和變量的類型為表蕊程,則表變量可在函數(shù)椒袍、存儲的過程和批處理。

創(chuàng)建表變量語法

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; 
  • 若有關(guān)聯(lián)查詢驹暑,則在FROM 子句,外部表必須使用一個別名辨赐,來引用變量优俘,如下面的示例中所示︰
SELECT EmployeeID, DepartmentID   
FROM @MyTableVar m  
JOIN Employee on (m.EmployeeID =Employee.EmployeeID AND  
   m.DepartmentID = Employee.DepartmentID);  
  • 表變量行為類似于本地變量。 有明確定義的作用域掀序。 這就是在其中聲明該變量的函數(shù)帆焕、存儲過程或批處理。在其范圍內(nèi)森枪,表可像常規(guī)表中使用變量视搏。 該變量可應(yīng)用于 SELECT、INSERT县袱、UPDATE 和 DELETE 語句中用到表或表的表達(dá)式的任何地方浑娜。在定義表變量的函數(shù)、存儲過程或批處理結(jié)束時式散,會自動清除表變量筋遭;
  • 表變量,使用系統(tǒng)內(nèi)存暴拄,讀寫速度快漓滔,但內(nèi)存是有一定限制,所有操作無日志乖篷。
  • 表變量是不需要考慮其他會話訪問的問題响驴,因此也不需要鎖機(jī)制,對于非常繁忙的系統(tǒng)來說撕蔼,避免鎖的使用可以減少一部分系統(tǒng)負(fù)載豁鲤;
  • 表變量并不是都存在于內(nèi)存中,表變量存放在內(nèi)存是有一定限制的鲸沮,如果表變量數(shù)據(jù)量超過閾值琳骡,會把內(nèi)存耗盡,然后使用TempDB的空間讼溺。
  • 由于表變量不會寫日志楣号,不會造成鎖開銷,不能在Declare之外創(chuàng)建主鍵索引等怒坯,因此表變量不會造成架構(gòu)的變化炫狱,從而不會造成重編譯。該存儲過程的執(zhí)行計劃已經(jīng)在創(chuàng)建存儲過程的時候生成了剔猿,因此之后執(zhí)行的存儲過程使用表變量不會造成執(zhí)行計劃的重編譯毕荐。

表變量的限制:

  • 不能對表變量執(zhí)行SELECT INTO語句,如:
SELECT select_list INTO table_variable;
  • 在SQL Server2000中艳馒,表變量也不能用于INSERT INTO table_variable EXEC stored_procedure這樣的語句中憎亚,而在2005之后的版本就開始支持這個用法了。
  • 在DECLARE后弄慰,不能再對表變量進(jìn)行更改第美,即無法進(jìn)行DDL操作;
  • 不能直接在表變量上創(chuàng)建索引(因為一旦你創(chuàng)建一個表變量之后陆爽,就不能對其進(jìn)行DDL語句了什往,這包括Create Index語句),但可以通過創(chuàng)建約束(主鍵慌闭、唯一)來建立索引别威;
  • 表變量聲明中的檢查約束躯舔、默認(rèn)值以及計算所得的列不能調(diào)用用戶定義的函數(shù)。
  • 表變量不支持變量之間的賦值操作省古;
  • 因為表變量具有有限的范圍粥庄,并不是持久的數(shù)據(jù)庫的一部分,它們不受事務(wù)回滾豺妓;
  • 表變量存在于內(nèi)存惜互,當(dāng)大數(shù)據(jù)量時,使用表變量的話就太耗內(nèi)存了琳拭;
  • 在表變量上不能創(chuàng)建非聚集索引(為 PRIMARY 或 UNIQUE 約束創(chuàng)建的系統(tǒng)索引除外)训堆。與具有非聚集索引的臨時表相比,這可能會影響查詢性能白嘁;
  • 表變量不具有數(shù)據(jù)分布的統(tǒng)計信息坑鱼,它們不會觸發(fā)重新編譯。在許多情況下絮缅,優(yōu)化器會在假定 table 變量沒有行的前提下生成查詢計劃姑躲。這樣不利于優(yōu)化器做出正確的執(zhí)行計劃,不適合數(shù)據(jù)量較大的情況盟蚣。
  • 如果表變量是在 EXEC 語句或 sp_executesql 存儲過程外創(chuàng)建的黍析,則不能使用 EXEC 語句或sp_executesql 存儲過程來運(yùn)行引用該表變量的動態(tài) SQL Server 查詢。由于表變量只能在它們的本地作用域中引用

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

-- 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
    --建一個表變量屎开,結(jié)構(gòu)與sp_GetBorrowRecord查詢出的結(jié)果集相同
     declare @Record table 
     (
      BookID int,                --書籍ID
      BookName varchar(100),    --書籍名稱
      TypeID int,                --書籍類別ID
      CardID int                --借書卡ID
      CardName varchar(100)        --借書人姓名
     )

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

臨時表

臨時表是臨時對象的一種,還有例如臨時存儲過程奄抽、臨時函數(shù)之類的臨時對象蔼两,臨時對象都存儲在tempdb中。

創(chuàng)建臨時表格式的兩種方式
①CREATE TABLE

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

②SELECT INTO
??使用SELECT INTO會自動生成臨時表搀菩,不需要事先創(chuàng)建

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

臨時表一般備注

  • 臨時表的用法(如INSERT、UPDATE破托、DELETE)和一般的表一樣肪跋;
  • 臨時表不能分區(qū)莲绰;
  • 如果在單個存儲過程或批處理中創(chuàng)建了多個臨時表引镊,則它們必須有不同的名稱来屠;
  • 如果使用到了臨時表,在存儲過程的最后務(wù)必將所有的臨時表顯式刪除瘟芝,先truncate table,然后drop table褥琐,這樣可以避免系統(tǒng)表的較長時間鎖定锌俱;
  • 如果本地臨時表由存儲過程創(chuàng)建或由多個用戶同時執(zhí)行的應(yīng)用程序創(chuàng)建,則數(shù)據(jù)庫引擎必須能夠區(qū)分由不同用戶創(chuàng)建的表敌呈。 為此贸宏,數(shù)據(jù)庫引擎在內(nèi)部為每個本地臨時表的表名追加一個數(shù)字后綴;
  • 臨時表以會話為邊界磕洪,除非使用 DROP TABLE 顯式刪除臨時表吭练,否則臨時表將在退出其作用域時由系統(tǒng)自動刪除;

局部臨時表會在下列情況下被Drop:
a析显、顯式調(diào)用Drop Table語句鲫咽;
b、當(dāng)存儲過程完成時谷异,將自動刪除在存儲過程中創(chuàng)建的本地臨時表分尸。
c、當(dāng)前會話結(jié)束歹嘹,在會話內(nèi)創(chuàng)建的所有局部臨時表都會被Drop箩绍;
全局臨時表會在下列情況下被Drop:
a、全局臨時表在創(chuàng)建此表的會話結(jié)束其他所有任務(wù)停止對其引用時將被自動刪除尺上。 換言之材蛛,當(dāng)創(chuàng)建全局臨時表的會話結(jié)束時,最后一條引用此表的 Transact-SQL 語句完成后怎抛,將自動刪除此表仰税。

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

臨時表的約束

  • 不能對臨時表進(jìn)行分區(qū);
  • 不能對臨時表加外鍵約束河绽;
  • 臨時表內(nèi)列的數(shù)據(jù)類型不能定義成沒有在TempDb中沒有定義自定義數(shù)據(jù)類型(自定義數(shù)據(jù)類型是數(shù)據(jù)庫級別的對象己单,而臨時表屬于TempDb)

同名臨時表
??從一般備注我們知道,如果本地臨時表由存儲過程創(chuàng)建耙饰,數(shù)據(jù)庫引擎在內(nèi)部為每個本地臨時表的表名追加一個數(shù)字后綴纹笼。當(dāng)存儲過程完成時,將自動除去在存儲過程中創(chuàng)建的本地臨時表。所以即使在存儲過程或觸發(fā)器中創(chuàng)建的本地臨時表的名稱可以與在調(diào)用存儲過程或觸發(fā)器之前創(chuàng)建的臨時表名稱相同苟跪,但是臨時表在會話中只是一個代號廷痘,在實際的系統(tǒng)臨時庫中,真實表名會自動處理件已,所以它們也是不同的笋额。
??在存儲過程或觸發(fā)器中創(chuàng)建的本地臨時表的名稱可以與在調(diào)用存儲過程或觸發(fā)器之前創(chuàng)建的臨時表名稱相同。 但是篷扩,如果查詢引用臨時表兄猩,而同時有兩個同名的臨時表,則不定義針對哪個表解析該查詢鉴未。
??嵌套存儲過程同樣可以創(chuàng)建與調(diào)用它的存儲過程所創(chuàng)建的臨時表同名的臨時表枢冤,套存儲過程中對表名的所有引用都被解釋為是針對該嵌套過程所創(chuàng)建的表。但是铜秆,為了對其進(jìn)行修改以解析為在嵌套過程中創(chuàng)建的表淹真,此表必須與調(diào)用過程創(chuàng)建的表具有相同的結(jié)構(gòu)和列名。

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  

下面是結(jié)果集:

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

【示例】

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

表變量與臨時表的對比

  • 臨時表是利用了硬盤(tempdb數(shù)據(jù)庫) 连茧,表名變量是占用內(nèi)存趟咆。在數(shù)據(jù)量比較大的時候,如果使用表變量梅屉,會把內(nèi)存耗盡值纱,然后使用TEMPDB的空間,這樣主要還是使用硬盤空間坯汤,但同時把內(nèi)存基本耗盡虐唠,增加了內(nèi)存調(diào)入調(diào)出的機(jī)會,反而降低速度惰聂。所以數(shù)據(jù)量比較少的時候可以使用表變量疆偿,數(shù)據(jù)量大時一般推薦使用臨時表。
  • 表變量缺省放在內(nèi)存搓幌,速度快杆故,因此建議觸發(fā)器、自定義函數(shù)用表變量溉愁;存儲過程看情況处铛,大部分用表變量;特殊的應(yīng)用,大數(shù)據(jù)量的場合用臨時表撤蟆。
  • 無表關(guān)聯(lián)操作奕塑,只作為中間集進(jìn)行數(shù)據(jù)處理,建議用表變量家肯;有表關(guān)聯(lián)龄砰,且不能確定數(shù)據(jù)量大小的情況下,建議用臨時表讨衣。
  • 表變量需要事先知道表結(jié)構(gòu)换棚,普通臨時表,只在當(dāng)前會話中可用與表變量相同into一下就可以了反镇,方便固蚤。
  • 全局臨時表的功能是表變量沒法達(dá)到的。全局臨時表可在多個會話中使用愿险。
  • 表變量不必刪除颇蜡,也就不會有命名沖突价说,臨時表特別是全局臨時表用的時候必須解決命名沖突辆亏。
  • 在存儲過程中使用表變量與使用臨時表相比,減少了存儲過程的重新編譯量鳖目。
  • 事務(wù)支持:臨時表:支持扮叨,表變量:不支持
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市领迈,隨后出現(xiàn)的幾起案子彻磁,更是在濱河造成了極大的恐慌,老刑警劉巖狸捅,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衷蜓,死亡現(xiàn)場離奇詭異,居然都是意外死亡尘喝,警方通過查閱死者的電腦和手機(jī)磁浇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來朽褪,“玉大人置吓,你說我怎么就攤上這事〉拊” “怎么了衍锚?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗤堰。 經(jīng)常有香客問我戴质,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任置森,我火速辦了婚禮斗埂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凫海。我一直安慰自己呛凶,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布行贪。 她就那樣靜靜地躺著漾稀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪建瘫。 梳的紋絲不亂的頭發(fā)上崭捍,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音啰脚,去河邊找鬼殷蛇。 笑死,一個胖子當(dāng)著我的面吹牛橄浓,可吹牛的內(nèi)容都是我干的粒梦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼荸实,長吁一口氣:“原來是場噩夢啊……” “哼匀们!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起准给,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤泄朴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后露氮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祖灰,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年畔规,在試婚紗的時候發(fā)現(xiàn)自己被綠了局扶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡油讯,死狀恐怖详民,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陌兑,我是刑警寧澤沈跨,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站兔综,受9級特大地震影響饿凛,放射性物質(zhì)發(fā)生泄漏狞玛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一涧窒、第九天 我趴在偏房一處隱蔽的房頂上張望心肪。 院中可真熱鬧,春花似錦纠吴、人聲如沸硬鞍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽固该。三九已至,卻和暖如春糖儡,著一層夾襖步出監(jiān)牢的瞬間伐坏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工握联, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留桦沉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓金闽,卻偏偏與公主長得像纯露,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呐矾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法苔埋,類相關(guān)的語法懦砂,內(nèi)部類的語法蜒犯,繼承相關(guān)的語法,異常的語法荞膘,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 從三月份找實習(xí)到現(xiàn)在罚随,面了一些公司,掛了不少羽资,但最終還是拿到小米淘菩、百度、阿里屠升、京東潮改、新浪、CVTE腹暖、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,184評論 11 349
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理汇在,服務(wù)發(fā)現(xiàn),斷路器脏答,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 我總想糕殉,從來沒有正兒八經(jīng)的有過任何一段戀愛關(guān)系的人亩鬼,才是真正的失戀者,可能全稱應(yīng)該是“缺失愛戀關(guān)系的人”阿蝶。a...
    一筐西紅柿閱讀 294評論 0 1
  • 大概是從很早了吧雳锋,電子游戲就已經(jīng)逐漸走進(jìn)我的生活,從單機(jī)的《仙劍奇?zhèn)b傳》到后來的《CS》,《魔獸爭霸》羡洁,再到...
    再見徹羅基閱讀 204評論 0 0