??? 關(guān)于表變量是什么(和表變量不是什么),以及和臨時(shí)表的比較讓很多人非常困惑。雖然網(wǎng)上已經(jīng)有了很多關(guān)于它們的文章,但我并沒(méi)有發(fā)現(xiàn)一篇比較全面的。在本篇文章中,我們將探索表變量和臨時(shí)表是什么(以及不是什么),然后我們通過(guò)使用臨時(shí)表和表變量對(duì)其解密。
?
表變量
??? 表變量在SQL Server 2000中首次被引入,那么,什么是表變量呢?微軟在 BOL (Declare @local_variable) 中定義其為一個(gè)類(lèi)型為表的變量。它的具體定義包括列定義,列名,數(shù)據(jù)類(lèi)型和約束。而在表變量中可以使用的約束包括主鍵約束,唯一約束,Null約束和Check約束(外鍵約束不能在表變量中使用).定義表變量的語(yǔ)句是和正常使用Create table定義表語(yǔ)句的子集。只是表變量通過(guò)DECLARE @local_variable 語(yǔ)句進(jìn)行定義。
?
通過(guò) 參考1 可以知道:
1) 表變量擁有特定作用域(在當(dāng)前批處理語(yǔ)句中,但不在任何當(dāng)前批處理語(yǔ)句調(diào)用的存儲(chǔ)過(guò)程和函數(shù)中),表變量在批處理結(jié)束后自動(dòng)被清除。
?
2) 參考6 中在"Recompilations Due to Certain Temporary Table Operations" 環(huán)節(jié)討論了臨時(shí)表在會(huì)導(dǎo)致存儲(chǔ)過(guò)程強(qiáng)制被重復(fù)編譯的各種原因,但這些原因并不適用于表變量。表變量和臨時(shí)表比起來(lái)會(huì)產(chǎn)生更少的存儲(chǔ)過(guò)程重編譯。
?
3) 針對(duì)表變量的事務(wù)僅僅在更新數(shù)據(jù)時(shí)生效,所以鎖和日志產(chǎn)生的數(shù)量會(huì)更少。
?
4) 由于表變量的作用域如此之小,而且不屬于數(shù)據(jù)庫(kù)的持久部分,所以事務(wù)回滾不會(huì)影響表變量。
?
??? 表變量可以在其作用域內(nèi)像正常的表一樣使用。更確切的說(shuō),表變量可以被當(dāng)成正常的表或者表表達(dá)式一樣在select,delete,update,insert語(yǔ)句中使用。但是表變量不能在類(lèi)似“SELECT select_list INTO table_variable” 這樣的語(yǔ)句中使用。而在SQL Server 2000中,表變量也不能被用于“INSERT INTO table_variable EXEC stored_procedure”這樣的語(yǔ)句中。
?
??? 表變量不能做如下事情:
??? 1.雖然表變量是一個(gè)變量,但是其不能賦值給另一個(gè)變量。
??? 2.check約束,默認(rèn)值,和計(jì)算列不能引用自定義函數(shù)。
??? 3.不能為約束命名。
??? 4.不能Truncate表變量
??? 5.不能向標(biāo)識(shí)列中插入顯式值(也就是說(shuō)表變量不支持SET IDENTITY_INSERT ON)
臨時(shí)表
??? 在深入臨時(shí)表之前,我們首先需要討論一下會(huì)話(Session),一個(gè)會(huì)話僅僅是一個(gè)客戶(hù)端到數(shù)據(jù)引擎的連接。在SQL Server Management Studio(SSMS)中,每一個(gè)查詢(xún)窗口都會(huì)和數(shù)據(jù)庫(kù)引擎建立連接。一個(gè)應(yīng)用程序可以和數(shù)據(jù)庫(kù)建立一個(gè)或多個(gè)連接,除此之外,應(yīng)用程序還可能建立連接后一直不釋放直到應(yīng)用程序結(jié)束,也可能使用完釋放連接需要時(shí)建立連接。
??? 那么,什么是臨時(shí)表?在 BOL (CREATE TABLE) 中,我們可以知道臨時(shí)表和以Create table語(yǔ)句創(chuàng)建的表有著相同的物理構(gòu)成,但臨時(shí)表與正常的表不同之處有:
?
1) 臨時(shí)表的名字不能超過(guò)116個(gè)字符,這是由于數(shù)據(jù)庫(kù)引擎為了辨別不同會(huì)話建立不同的臨時(shí)表,所以會(huì)自動(dòng)在臨時(shí)表的名字后附加一串
?
2) 局部臨時(shí)表(以“#”開(kāi)頭命名的)作用域僅僅在當(dāng)前的連接內(nèi),從在存儲(chǔ)過(guò)程中建立局部臨時(shí)表的角度來(lái)看,局部臨時(shí)表會(huì)在下列情況被Drop:
??? a.顯式調(diào)用DROP Table語(yǔ)句
??? b.當(dāng)局部臨時(shí)表在存儲(chǔ)過(guò)程內(nèi)被創(chuàng)建時(shí),存儲(chǔ)過(guò)程結(jié)束也就意味著局部臨時(shí)表被DROP
??? c.當(dāng)前會(huì)話結(jié)束,在會(huì)話內(nèi)創(chuàng)建的所有局部臨時(shí)表都會(huì)被Drop
?
3) 全局臨時(shí)表(以“##”開(kāi)頭命名的)在所有的會(huì)話內(nèi)可見(jiàn),所以在創(chuàng)建全局臨時(shí)表之前首先檢查其是否存在,否則如果已經(jīng)存在,你將會(huì)得到重復(fù)創(chuàng)建對(duì)象的錯(cuò)誤.
??? a.全局臨時(shí)表會(huì)在創(chuàng)建其的會(huì)話結(jié)束后被DROP,其它會(huì)話將不能對(duì)全局臨時(shí)表進(jìn)行引用。
??? b.引用是在語(yǔ)句級(jí)別進(jìn)行,比如說(shuō)下面例子:
??????? i.建立新的查詢(xún)窗口,運(yùn)行如下語(yǔ)句:
create table ##temp (RowID int )
??????? ii.再次開(kāi)啟一個(gè)新的查詢(xún)創(chuàng)建,使用如下語(yǔ)句每5秒中對(duì)全局臨時(shí)表進(jìn)行引用
while 1=1 begin select * from ##temp waitfor delay '00:00:05' end
?????? iii.回到第一個(gè)窗口,關(guān)閉窗口
?????? iv.在下一個(gè)循環(huán)引用全局臨時(shí)表時(shí),將產(chǎn)生錯(cuò)誤
?
4) 不能對(duì)臨時(shí)表進(jìn)行分區(qū)。
?
5) 不能對(duì)臨時(shí)表加外鍵約束
6) 臨時(shí)表內(nèi)列的數(shù)據(jù)類(lèi)型不能定義成沒(méi)有在TempDb中沒(méi)有定義自定義數(shù)據(jù)類(lèi)型(自定義數(shù)據(jù)類(lèi)型是數(shù)據(jù)庫(kù)級(jí)別的對(duì)象,而臨時(shí)表屬于TempDb),由于TempDb在每次SQL Server重啟后會(huì)被自動(dòng)創(chuàng)建,所以你必須使用 startup stored procedure 來(lái)為T(mén)empDb創(chuàng)建自定義數(shù)據(jù)類(lèi)型。你也可以通過(guò)修改Model數(shù)據(jù)庫(kù)來(lái)達(dá)到這一目標(biāo)。
?
7) XML列不能定義成XML集合的形式,除非這個(gè)集合已經(jīng)在TempDb中定義
?
??? 臨時(shí)表既可以通過(guò)Create Table語(yǔ)句創(chuàng)建,也可以通過(guò)”SELECT <select_list> INTO #table”語(yǔ)句創(chuàng)建。你還可以針對(duì)臨時(shí)表使用”INSERT INTO #table EXEC stored_procedure”這樣的語(yǔ)句。
??? 臨時(shí)表可以擁有命名的約束和索引。但是,當(dāng)兩個(gè)用戶(hù)在同一時(shí)間調(diào)用同一存儲(chǔ)過(guò)程時(shí),將會(huì)產(chǎn)生”There is already an object named ‘<objectname>’ in the database”這樣的錯(cuò)誤。所以最好的做法是不用為建立的對(duì)象進(jìn)行命名,而使用系統(tǒng)分配的在TempDb中唯一的。6
??? 參考6 談?wù)摿撕芏嘤捎谂R時(shí)表而導(dǎo)致的存儲(chǔ)過(guò)程重編譯的原因以及避免的方法。
?
誤區(qū)
??? 誤區(qū)1.表變量?jī)H僅在內(nèi)存中。
??? 誤區(qū)2.臨時(shí)表僅僅存儲(chǔ)在物理介質(zhì)中
??? 這兩種觀點(diǎn)都是明顯的誤區(qū),在參考1的Q4節(jié)。表變量都是在TempDb數(shù)據(jù)庫(kù)中創(chuàng)建,因?yàn)楸碜兞看鎯?chǔ)的數(shù)據(jù)有可能超過(guò)物理內(nèi)存。除此之外,我們發(fā)現(xiàn)只要內(nèi)存足夠,表變量和臨時(shí)表都會(huì)在內(nèi)存中創(chuàng)建和處理。它們也同樣可以在任何時(shí)間被存入磁盤(pán)。
??? 如何證明這點(diǎn)?請(qǐng)看下面代碼(在SQL Server 2000到2008中都有效)
-- make a list of all of the user tables currently active in the
-- TempDB database
if object_id( 'tempdb..#tempTables' ) is not null drop table #tempTables
select name into #tempTables from tempdb..sysobjects where type = 'U'
-- prove that even this new temporary table is in the list.
-- Note the suffix at the end of it to uniquely identify the table across sessions.
select * from #tempTables where name like '#tempTables%'
GO
-- create a table variable
declare @MyTableVariable table (RowID int )
-- show all of the new user tables in the TempDB database.
select name from tempdb..sysobjects
where type = 'U' and name not in ( select name from #tempTables)
?
??? 還有一些“證明”臨時(shí)表僅僅存在于內(nèi)存中謬誤,下面我來(lái)指出其中一個(gè):
??? 注意表變量的名字是系統(tǒng)分配的,表變量的第一個(gè)字符”@”并不是一個(gè)字母,所以它并不是一個(gè)有效的變量名。系統(tǒng)會(huì)在TempDb中為表變量創(chuàng)建一個(gè)系統(tǒng)分配的名稱(chēng),所以任何在sysobjects或sys.tables查找表變量的方法都會(huì)失敗。
??? 正確的方法應(yīng)該是我前面例子中的方法,我看到很多人使用如下查詢(xún)查表變量:
select * from sysobjects where name like '#tempTables%'
??? 上述代碼看上去貌似很好用,但會(huì)產(chǎn)生多用戶(hù)的問(wèn)題。你建立兩個(gè)連接,在第一個(gè)連接中創(chuàng)建臨時(shí)表,在第二個(gè)窗口中運(yùn)行上面的語(yǔ)句能看到第一個(gè)連接創(chuàng)建的臨時(shí)表,如果你在第二個(gè)連接中嘗試操作這個(gè)臨時(shí)表,那么可能會(huì)產(chǎn)生錯(cuò)誤,因?yàn)檫@個(gè)臨時(shí)表不屬于你的會(huì)話。
?
誤區(qū)3.表變量不能擁有索引。
??? 這個(gè)誤區(qū)也同樣錯(cuò)誤。雖然一旦你創(chuàng)建一個(gè)表變量之后,就不能對(duì)其進(jìn)行DDL語(yǔ)句了,這包括Create Index語(yǔ)句。然而你可以在表變量定義的時(shí)候?yàn)槠鋭?chuàng)建索引)比如如下語(yǔ)句.
declare @MyTableVariable table (RowID intPRIMARY KEY CLUSTERED )
??? 這個(gè)語(yǔ)句將會(huì)創(chuàng)建一個(gè)擁有聚集索引的表變量。由于主鍵有了對(duì)應(yīng)的聚集索引,所以一個(gè)系統(tǒng)命名的索引將會(huì)被創(chuàng)建在RowID列上。
??? 下面的例子演示你可以在一個(gè)表變量的列上創(chuàng)建唯一約束以及如何建立符合索引。
declare @temp TABLE ( RowID int NOT NULL , ColA int NOT NULL , ColB char (1) UNIQUE , PRIMARY KEY CLUSTERED (RowID, ColA))
1) SQL 并不能為表變量建立統(tǒng)計(jì)信息,就像其能為臨時(shí)表建立統(tǒng)計(jì)信息一樣。這意味著對(duì)于表變量,執(zhí)行引擎認(rèn)為其只有1行,這也意味著針對(duì)表變量的執(zhí)行計(jì)劃并不是最優(yōu)。雖然估計(jì)的執(zhí)行計(jì)劃對(duì)于表變量和臨時(shí)表都為1,但是實(shí)際的執(zhí)行計(jì)劃對(duì)于臨時(shí)表會(huì)根據(jù)每次存儲(chǔ)過(guò)程的重編譯而改變(看參考1,Q2部分).如果臨時(shí)表不存在,在生成執(zhí)行計(jì)劃的時(shí)候會(huì)產(chǎn)生錯(cuò)誤。
?
2) 前面提到,一定建立表變量后就無(wú)法對(duì)其進(jìn)行DDL語(yǔ)句操作。因此如果需要為表建立索引或者加一列,你需要臨時(shí)表。
?
3) 表變量不能使用select …into語(yǔ)句,而臨時(shí)表可以
?
4) 在SQL Server 2008中,你可以將表變量作為參數(shù)傳入存儲(chǔ)過(guò)程。但是臨時(shí)表不行。在SQL Server 2000和2005中表變量也不行。
?
5) 作用域:表變量?jī)H僅在當(dāng)前的批處理中有效,并且對(duì)任何在其中嵌套的存儲(chǔ)過(guò)程等不可見(jiàn)。局部臨時(shí)表只在當(dāng)前會(huì)話中有效,這也包括嵌套的存儲(chǔ)過(guò)程。但對(duì)父存儲(chǔ)過(guò)程不可見(jiàn)。全局臨時(shí)表可以在任何會(huì)話中可見(jiàn),但是會(huì)隨著創(chuàng)建其的會(huì)話終止而DROP,其它會(huì)話這時(shí)就不能再引用全局臨時(shí)表。
?
6) 排序規(guī)則:表變量使用當(dāng)前數(shù)據(jù)庫(kù)的排序規(guī)則,臨時(shí)表使用TempDb的排序規(guī)則。如果它們不兼容,你還需要在查詢(xún)或者表定義中進(jìn)行指定(參考7. Table Variables and Temporary Tables )
?
7) 你如果希望在動(dòng)態(tài)SQL中使用表變量,你必須在動(dòng)態(tài)SQL中定義表變量。而臨時(shí)表可以提前定義,在動(dòng)態(tài)SQL中進(jìn)行引用。
?
說(shuō)了這么多,那么,我該如何選擇呢?
??? 微軟 推薦 使用表變量(看參考4),如果表中的行數(shù)非常小,則使用表變量。很多”網(wǎng)絡(luò)專(zhuān)家”會(huì)告訴你100是一個(gè)分界線,因?yàn)檫@是統(tǒng)計(jì)信息創(chuàng)建查詢(xún)計(jì)劃效率高低的開(kāi)始。但是我還是希望告訴你針對(duì)你的特定需求對(duì)臨時(shí)表和表變量進(jìn)行測(cè)試。很多人在自定義函數(shù)中使用表變量,如果你需要在表變量中使用主鍵和唯一索引,你會(huì)發(fā)現(xiàn)包含數(shù)千行的表變量也依然性能卓越。但如果你需要將表變量和其它表進(jìn)行join,你會(huì)發(fā)現(xiàn)由于不精準(zhǔn)的執(zhí)行計(jì)劃,性能往往會(huì)非常差。
??? 為了證明這點(diǎn),請(qǐng)看本文的附件。附件中代碼創(chuàng)建了表變量和臨時(shí)表.并裝入了AdventureWorks數(shù)據(jù)庫(kù)的Sales.SalesOrderDetail表。為了得到足夠的測(cè)試數(shù)據(jù),我將這個(gè)表中的數(shù)據(jù)插入了10遍。然后以ModifiedDate 列作為條件將臨時(shí)表和表變量與原始的Sales.SalesOrderDetail表進(jìn)行了Join操作,從統(tǒng)計(jì)信息來(lái)看IO差別顯著。從時(shí)間來(lái)看表變量做join花了50多秒,而臨時(shí)表僅僅花了8秒。
??? 如果你需要在表建立后對(duì)表進(jìn)行DLL操作,那么選擇臨時(shí)表吧。
??? 臨時(shí)表和表變量有很多類(lèi)似的地方。所以有時(shí)候并沒(méi)有具體的細(xì)則規(guī)定如何選擇哪一個(gè)。對(duì)任何特定的情況,你都需要考慮其各自?xún)?yōu)缺點(diǎn)并做一些性能測(cè)試。下面的表格會(huì)讓你比較其優(yōu)略有了更詳細(xì)的參考。
?
總結(jié)
特性 | 表變量 | 臨時(shí)表 |
作用域 | 當(dāng)前批處理 | 當(dāng)前會(huì)話,嵌套存儲(chǔ)過(guò)程,全局:所有會(huì)話 |
使用場(chǎng)景 | 自定義函數(shù),存儲(chǔ)過(guò)程,批處理 | 自定義函數(shù),存儲(chǔ)過(guò)程,批處理 |
創(chuàng)建方式 | DECLARE statement only.只能通過(guò)DECLEARE語(yǔ)句創(chuàng)建 |
CREATE TABLE 語(yǔ)句 SELECT INTO 語(yǔ)句. |
表名長(zhǎng)度 | 最多128字節(jié) | 最多116字節(jié) |
列類(lèi)型 |
可以使用自定義數(shù)據(jù)類(lèi)型 可以使用XML集合 |
自定義數(shù)據(jù)類(lèi)型和XML集合必須在TempDb內(nèi)定義 |
Collation | 字符串排序規(guī)則繼承自當(dāng)前數(shù)據(jù)庫(kù) | 字符串排序規(guī)則繼承自TempDb數(shù)據(jù)庫(kù) |
索引 | 索引必須在表定義時(shí)建立 | 索引可以在表創(chuàng)建后建立 |
約束 | PRIMARY KEY, UNIQUE, NULL, CHECK約束可以使用,但必須在表建立時(shí)聲明 | PRIMARY KEY, UNIQUE, NULL, CHECK. 約束可以使用,可以在任何時(shí)后添加,但不能有外鍵約束 |
表建立后使用DDL (索引,列) | 不允許 | 允許. |
數(shù)據(jù)插入方式 | INSERT 語(yǔ)句 (SQL 2000: 不能使用INSERT/EXEC). |
INSERT 語(yǔ)句, 包括 INSERT/EXEC. SELECT INTO 語(yǔ)句. |
Insert explicit values into identity columns (SET IDENTITY_INSERT). | 不支持SET IDENTITY_INSERT語(yǔ)句 | 支持SET IDENTITY_INSERT語(yǔ)句 |
Truncate table | 不允許 | 允許 |
析構(gòu)方式 | 批處理結(jié)束后自動(dòng)析構(gòu) |
顯式調(diào)用 DROP TABLE 語(yǔ)句.
當(dāng)前會(huì)話結(jié)束自動(dòng)析構(gòu) (全局臨時(shí)表: 還包括當(dāng)其它會(huì)話語(yǔ)句不在引用表.) |
事務(wù) | 只會(huì)在更新表的時(shí)候有事務(wù),持續(xù)時(shí)間比臨時(shí)表短 | 正常的事務(wù)長(zhǎng)度,比表變量長(zhǎng) |
存儲(chǔ)過(guò)程重編譯 | 否 | 會(huì)導(dǎo)致重編譯 |
回滾 | 不會(huì)被回滾影響 | 會(huì)被回滾影響 |
統(tǒng)計(jì)數(shù)據(jù) | 不創(chuàng)建統(tǒng)計(jì)數(shù)據(jù),所以所有的估計(jì)行數(shù)都為1,所以生成執(zhí)行計(jì)劃會(huì)不精準(zhǔn) | 創(chuàng)建統(tǒng)計(jì)數(shù)據(jù),通過(guò)實(shí)際的行數(shù)生成執(zhí)行計(jì)劃。 |
作為參數(shù)傳入存儲(chǔ)過(guò)程 | 僅僅在SQL Server2008, 并且必須預(yù)定義 user-defined table type. | 不允許 |
顯式命名對(duì)象 (索引, 約束). | 不允許 | 允許,但是要注意多用戶(hù)的問(wèn)題 |
動(dòng)態(tài)SQL | 必須在動(dòng)態(tài)SQL中定義表變量 | 可以在調(diào)用動(dòng)態(tài)SQL之前定義臨時(shí)表 |
?
參考:
1) INF: Frequently Asked Questions - SQL Server 2000 - Table Variables
2) T-SQL BOL (SQL 2000), table data type
3) T-SQL BOL (SQL 2008), Declare @local_variable
4) T-SQL BOL (SQL 2008), CREATE TABLE
5) Table-Valued Parameters (Database Engine)
6) Troubleshooting stored procedure recompilation
7) Local Temporary Tables and Table Variables
9) Data Definition Language (DDL)
其它值得閱讀的文章:
1) Things You Didn’t Know About Temp Tables and Table Variables
?
?
?
-----------------------------------------------------------------------
原文鏈接: http://www.sqlservercentral.com/articles/Temporary+Tables/66720/
Translated by: CareySon
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
