SQL Server 表變量和臨時表的區別

1、表變量

  表變量在SQL Server 2000中首次被引入。表變量的具體定義包括列定義,列名,數據類型和約束。而在表變量中可使用的約束包括主鍵約束,惟一約束,NULL約束和CHECK約束(外鍵約束不能在表變量中使用)。定義表變量的語句是和正常使用Create Table定義表語句的子集。只是表變量經過DECLARE @local_variable語句進行定義。html

  表變量的特徵:數據庫

  1. 表變量擁有特定做用域(在當前批處理語句中,但不在任何當前批處理語句調用的存儲過程和函數中),表變量在批處理結束後自動被清除
  2. 表變量較臨時表產生更少的存儲過程重編譯。
  3. 針對表變量的事務僅僅在更新數據時生效,因此鎖和日誌產生的數量會更少。
  4. 因爲表變量的做用域如此之小,並且不屬於數據庫的持久部分,因此事務回滾不會影響表變量。

  表變量能夠在其做用域內像正常的表同樣使用。更確切的說,表變量能夠被當成正常的表或者表表達式同樣在SELECT,DELETE,UPDATE,INSERT語句中使用,可是表變量不能在相似"SELECT select_list INTO table_variable"這樣的語句中使用。而在SQL Server2000中,表變量也不能用於INSERT INTO table_variable EXEC stored_procedure這樣的語句中。網絡

  表變量不能作以下事情:ide

  1. 雖然表變量是一個變量,可是其不能賦值給另外一個變量。
  2. check約束,默認值和計算列不能引用自定義函數。
  3. 不能爲約束命名。
  4. 不能Truncate表變量。
  5. 不能向標識列中插入顯式值(也就是說表變量不支持SET IDENTITY_INSERT ON)

   下面來玩玩表變量吧。函數

  定義一個表變量,插入一條數據,而後查詢:性能

  DECLARE @tb1 Table
  (
   Id int,
   Name varchar(20),
   Age int
  )

  INSERT INTO @tb1 VALUES(1,'劉備',22)

  SELECT * FROM @tb1

  輸出結果以下:測試

  

   再來試試一些不符合要求的狀況,例如添加表變量後,添加約束,並對約束命名:spa

  ALTER TABLE @tb1
    ADD CONSTRAINT CN_AccountAge
    CHECK 
    (Account_Age > 18);    -- 插入年齡必須大於18

  SQL Server提示錯誤以下:日誌

  

  SQL Server不支持定義表變量時對Constraint命名,也不支持定義表變量後,對其建Constraint。code

   更多的不容許,請查看上面的要求。

2、臨時表

  在深刻臨時表以前,咱們要了解一下會話(Session),一個會話僅僅是一個客戶端到數據引擎的鏈接。在SQL Server Management Studio中,每個查詢窗口都會和數據庫引擎創建鏈接。一個應用程序能夠和數據庫創建一個或多個鏈接,除此以外,應用程序還可能創建鏈接後一直不釋放知道應用程序結束,也可能使用完釋放鏈接須要時創建鏈接。

  臨時表和Create Table語句建立的表有着相同的物理工程,但臨時表與正常的表不一樣之處有:

  一、臨時表的名稱不能超過116個字符,這是因爲數據庫引擎爲了辨別不一樣會話創建不一樣的臨時表,因此會自動在臨時表的名字後附加一串。

  二、局部臨時表(以"#"開頭命名的)做用域僅僅在當前的鏈接內,從在存儲過程當中創建局部臨時表的角度來看,局部臨時表會在下列狀況下被Drop:
    a、顯示調用Drop Table語句
    b、當局部臨時表在存儲過程內被建立時,存儲過程結束也就意味着局部臨時表被Drop。
    c、當前會話結束,在會話內建立的全部局部臨時表都會被Drop。

  三、全局臨時表(以"##"開頭命名的)在全部的會話內可見,因此在建立全局臨時表以前首先檢查其是否存在,不然若是已經存在,你將會獲得重複建立對象的錯誤。
    a、全局臨時表會在建立其的會話結束後被Drop,Drop後其餘會話將不能對全局臨時表進行引用。
    b、引用是在語句級別進行,如:
      1.新建查詢窗口,運行語句:

  CREATE TABLE ##temp(RowID int)
  INSERT INTO ##temp VALUES(3)

      2.再次新建一個查詢窗口,每5秒引用一次全局臨時表

  While 1=1 
  BEGIN
  SELECT * FROM ##temp
  WAITFOR delay '00:00:05'
  END

      3.回到第一個窗口,關閉窗口。
      4.下一次第二個窗口引用時,將產生錯誤。

       

  四、不能對臨時表進行分區。

  五、不能對臨時表加外鍵約束。

  六、臨時表內列的數據類型不能定義成沒有在TempDb中沒有定義自定義數據類型(自定義數據類型是數據庫級別的對象,而臨時表屬於TempDb)。因爲TempDb在每次SQL Server重啓後會被自動建立,因此你必須使用startup stored procedure來爲TempDb建立自定義數據類型。你也能夠經過修改Model數據庫來達到這一目標。

  七、XML列不能定義成XML集合的形式,除非這個集合已經在TempDb中定義。

  臨時表既能夠經過Create Table語句建立,也能夠經過"SELECT <select_list> INTO #table"語句建立。你還能夠針對臨時表用"INSERT INTO #table EXEC stored_procedure"這樣的語句。
  臨時表能夠擁有命名的約束和索引。可是,當兩個用戶在同一時間調用同一存儲過程時,將會產生」There is already an object named ‘<objectname>’ in the database」這樣的錯誤。因此最好的作法是不用爲創建的對象進行命名,而使用系統分配的在TempDb中惟一的。

3、誤區

  誤區1.表變量僅僅在內存中。

  誤區2.臨時表僅僅存儲在物理介質中。

  這兩種觀點都是錯誤的,只有內存足夠,表變量和臨時表都會在內存中建立和處理。他們也一樣能夠在任什麼時候間被存入磁盤。

  注意表變量的名字是系統分配的,表變量的第一個字符」@」並非一個字母,因此它並非一個有效的變量名。系統會在TempDb中爲表變量建立一個系統分配的名稱,因此任何在sysobjects或sys.tables查找表變量的方法都會失敗。

  正確的方法應該是我前面例子中的方法,我看到不少人使用以下查詢查表變量:

  select * from sysobjects where name like'#tempTables%'

  上述代碼看上去貌似很好用,但會產生多用戶的問題。你創建兩個鏈接,在第一個鏈接中建立臨時表,在第二個窗口中運行上面的語句能看到第一個鏈接建立的臨時表,若是你在第二個鏈接中嘗試操做這個臨時表,那麼可能會產生錯誤,由於這個臨時表不屬於你的會話。

  誤區3.表變量不能擁有索引。

    這個誤區也一樣錯誤。雖然一旦你建立一個表變量以後,就不能對其進行DDL語句了,這包括Create Index語句。然而你能夠在表變量定義的時候爲其建立索引)好比以下語句。

  declare @MyTableVariable table (RowID intPRIMARY KEY CLUSTERED) 

    這個語句將會建立一個擁有彙集索引的表變量。因爲主鍵有了對應的彙集索引,因此一個系統命名的索引將會被建立在RowID列上。

    下面的例子演示你能夠在一個表變量的列上建立惟一約束以及如何創建複合索引。

   declare @temp TABLE (
     RowID int NOT NULL,
     ColA int NOT NULL,
     ColB char(1)UNIQUE,
     PRIMARY KEY CLUSTERED(RowID, ColA))

  1) SQL 並不能爲表變量創建統計信息,就像其能爲臨時表創建統計信息同樣。這意味着對於表變量,執行引擎認爲其只有1行,這也意味着針對表變量的執行計劃並非最優。雖然估計的執行計劃對於表變量和臨時表都爲1,可是實際的執行計劃對於臨時表會根據每次存儲過程的重編譯而改變。若是臨時表不存在,在生成執行計劃的時候會產生錯誤。

  2) 一旦創建表變量後就沒法對其進行DDL語句操做。所以若是須要爲表創建索引或者加一列,你須要臨時表。

  3) 表變量不能使用select …into語句,而臨時表能夠。

  4) 在SQL Server 2008中,你能夠將表變量做爲參數傳入存儲過程。可是臨時表不行。在SQL Server 2000和2005中表變量也不行。

  5) 做用域:表變量僅僅在當前的批處理中有效,而且對任何在其中嵌套的存儲過程等不可見。局部臨時表只在當前會話中有效,這也包括嵌套的存儲過程。但對父存儲過程不可見。全局臨時表能夠在任何會話中可見,可是會隨着建立其的會話終止而DROP,其它會話這時就不能再引用全局臨時表。

  6) 排序規則:表變量使用當前數據庫的排序規則,臨時表使用TempDb的排序規則。若是它們不兼容,你還須要在查詢或者表定義中進行指定。

  7) 你若是但願在動態SQL中使用表變量,你必須在動態SQL中定義表變量。而臨時表能夠提早定義,在動態SQL中進行引用。

4、如何選擇

  微軟推薦使用表變量,若是表中的行數很是小,則使用表變量。不少」網絡專家」會告訴你100是一個分界線,由於這是統計信息建立查詢計劃效率高低的開始。可是我仍是但願告訴你針對你的特定需求對臨時表和表變量進行測試。不少人在自定義函數中使用表變量,若是你須要在表變量中使用主鍵和惟一索引,你會發現包含數千行的表變量也依然性能卓越。但若是你須要將表變量和其它表進行join,你會發現因爲不精準的執行計劃,性能每每會很是差。

  爲了證實這點,請看本文的附件。附件中代碼建立了表變量和臨時表.並裝入了AdventureWorks數據庫的Sales.SalesOrderDetail表。爲了獲得足夠的測試數據,我將這個表中的數據插入了10遍。而後以ModifiedDate 列做爲條件將臨時表和表變量與原始的Sales.SalesOrderDetail表進行了Join操做,從統計信息來看IO差異顯著。從時間來看錶變量作join花了50多秒,而臨時表僅僅花了8秒。

  若是你須要在表創建後對錶進行DLL操做,那麼選擇臨時表吧。

  臨時表和表變量有不少相似的地方。因此有時候並無具體的細則規定如何選擇哪個。對任何特定的狀況,你都須要考慮其各自優缺點並作一些性能測試。下面的表格會讓你比較其優略有了更詳細的參考。

5、總結

特性 表變量 臨時表
做用域 當前批處理 當前會話,嵌套存儲過程,全局:全部會話
使用場景 自定義函數,存儲過程,批處理 自定義函數,存儲過程,批處理
建立方式 DECLARE statement only.只能經過DECLEARE語句建立

CREATE TABLE 語句

SELECT INTO 語句.

表名長度 最多128字節 最多116字節
列類型

可使用自定義數據類型

可使用XML集合

自定義數據類型和XML集合必須在TempDb內定義
Collation 字符串排序規則繼承自當前數據庫 字符串排序規則繼承自TempDb數據庫
索引 索引必須在表定義時創建 索引能夠在表建立後創建
約束 PRIMARY KEY, UNIQUE, NULL, CHECK約束可使用,但必須在表創建時聲明 PRIMARY KEY, UNIQUE, NULL, CHECK. 約束可使用,能夠在任什麼時候後添加,但不能有外鍵約束
表創建後使用DDL (索引,列) 不容許 容許.
數據插入方式 INSERT 語句 (SQL 2000: 不能使用INSERT/EXEC).

INSERT 語句, 包括 INSERT/EXEC.

SELECT INTO 語句.

Insert explicit values into identity columns (SET IDENTITY_INSERT). 不支持SET IDENTITY_INSERT語句 支持SET IDENTITY_INSERT語句
Truncate table 不容許 容許
析構方式 批處理結束後自動析構 顯式調用 DROP TABLE 語句. 
當前會話結束自動析構 (全局臨時表: 還包括當其它會話語句不在引用表.)
事務 只會在更新表的時候有事務,持續時間比臨時表短 正常的事務長度,比表變量長
存儲過程重編譯 會致使重編譯
回滾 不會被回滾影響 會被回滾影響
統計數據 不建立統計數據,因此全部的估計行數都爲1,因此生成執行計劃會不精準 建立統計數據,經過實際的行數生成執行計劃。
做爲參數傳入存儲過程 僅僅在SQL Server2008, 而且必須預約義 user-defined table type. 不容許
顯式命名對象 (索引, 約束). 不容許 容許,可是要注意多用戶的問題
動態SQL 必須在動態SQL中定義表變量 能夠在調用動態SQL以前定義臨時表

 

出處:http://www.cnblogs.com/CareySon/archive/2012/06/11/TableVariableAndTempTable.html

相關文章
相關標籤/搜索