SQL Server 表變量和臨時表系列之概念篇

問題引入

“菜鳥啊,最近我看到阿里雲開發者論壇的數據庫RDS中有人在提SQL Server表變量和臨時表如何選擇的問題,你去深入探討下這個問題吧,解答解答他們的疑惑吧”,老鳥又開始爲菜鳥找活幹了。
“鳥哥啊,關於表變量和臨時表使用選擇的問題啊,向來行業裏爭論不休,我比較擔心我們的觀點被人家拍磚啊”。
“鳥啊,有爭論才說明這個問題有價值啊,所以我們才更應該去弄清楚,道明白啊”。反正老鳥總會找到合適的理由。
“那好吧,要把這個問題要刨根問底,我們需要分四篇文章來把這個問題理清楚。”,菜鳥掰着手指頭就數了出來:
 表變量和臨時表基本概念
 表變量和臨時表的對比
 表變量和臨時表認知誤區
 表變量和臨時表的選擇

什麼是表變量

關於什麼是SQL Server的表變量,我們分別從表變量的定義、表變量的作用和表變量的使用三個角度來看看什麼是表變量。

表變量定義

表變量,是微軟至SQL Server 2000以來引入的概念,從名稱我們就可以很容易看出,表變量本質是一個變量,只是它具有了正式表對象的很多屬性。比如:它有表字段、字段數據類型、字段寬度、主鍵、唯一約束、NULL、NOT NULL約束、CHECK和DEFAULT約束。但是,表變量不支持約束命名,不支持索引,不支持外鍵,不支持表變量定義後的任何表變量結構的修改,僅可做數據的DML操作。

表變量的作用

當我們需要在當前會話臨時緩存少量的中間數據結果集,供當前會話多次使用這同一數據集或者同一數據結果集的一部分時,我們可以考慮使用表變量,表變量中的數據是緩存在內存中(大部分情況下如此,也有極少情況例外,我們後面的文章會講到)。注意這裏是少量數據集,不是大量結果集,如果非要給一個參照經驗值的話,個人建議是最好不要超過10萬條數據記錄,所佔的空間大小不要超過100MB。

表變量的使用

關於表變量作用,在此我們以一個例子來說明。在這個例子中,我們定義了一個表變量來暫時存放商品的基本屬性信息,然後INSERT了三條數據,緊接着對其中一條數據做UPDATE操作,再接着DELETE了一條數據,最後我們SELECT了整個表變量存放的數據。

USE tempdb
GO

DECLARE
    @tb_table TABLE(
RowID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
,ProductName NVARCHAR(50) NOT NULL UNIQUE
,Length DECIMAL(4,2) NOT NULL CHECK(Length>0.0)
,Windth DECIMAL(4,2) NOT NULL CHECK(Windth>0.0)
,Height DECIMAL(4,2) NOT NULL CHECK(Height>0.0)
,Dimension AS (Length * Windth * Height)
,Indate DATETIME NOT NULL  DEFAULT(GETDATE())
);

INSERT INTO @tb_table(ProductName, Length, Windth, Height) VALUES('A', 0.1, 0.2, 0.3);
INSERT INTO @tb_table(ProductName, Length, Windth, Height) VALUES('B', 0.4, 0.5, 0.6);
INSERT INTO @tb_table(ProductName, Length, Windth, Height) VALUES('C', 0.7, 0.8, 0.9);

UPDATE A
SET Length = 2.5
FROM @tb_table AS A
WHERE RowID = 1
;

DELETE TOP(1) A
FROM @tb_table AS A
WHERE RowID = 2;

SELECT * FROM @tb_table;

從這個例子,我們看到了表變量所具有的正式表對象的屬性,表變量是如何定義的,以及DML操作,在當前會話結束後,表變量會被SQL Server自動回收。
這裏需要特別提醒下,SQL Server系統不允許我們像正式表對象那樣對約束進行顯示命名,SQL Server會報告錯誤。比如,定義表變量代碼:

USE tempdb
GO
DECLARE
    @tb_table TABLE(
RowID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
,ProductName NVARCHAR(50) NOT NULL UNIQUE
,Length DECIMAL(8,2) NOT NULL
,Windth DECIMAL(8,2) NOT NULL
,Height DECIMAL(8,2) NOT NULL
,Indate DATETIME NOT NULL  CONSTRAINT DF_tbTable DEFAULT(GETDATE())
,CONSTRAINT CK_Windth CHECK(Windth>0.0)
);

報錯信息如下:

Msg 156, Level 15, State 1, Line 10
Incorrect syntax near the keyword 'CONSTRAINT'.

什麼是臨時表

在看完什麼是表變量以後,我們還是分別從臨時表定義、臨時表的作用和臨時表的使用三個角度來看看什麼是SQL Server的臨時表。

臨時表定義

SQL Server的臨時表是一種特殊的表,表名字是以#或者##打頭。無論臨時表在哪個數據庫下創建,SQL Server均把臨時表結構信息和數據存儲在Tempdb數據庫下。
以#打頭的臨時表稱爲局部臨時表,這種類型的臨時表僅當前進程可見,其他進程不可訪問,生命週期會隨着當前連接進程的關閉而消亡。
以##打頭的臨時表稱爲全局,此類型的臨時表對所有進程可見,當前進程和其他進程均可訪問,生命週期是所有使用到全局臨時表的連接完全關閉後,臨時表消亡。

臨時表的作用

臨時表的作用和表變量類似,均是用於暫時緩存數據。臨時表中的數據會被儲存在Tempdb的物理文件磁盤上,當需要數據讀取時,SQL Server會將臨時表中數據從磁盤文件讀入SQL Server Buffer Pool中,然後返回給客戶端。因此,臨時表對數據的存儲和讀取會有物理的IO Write和IO Read的。臨時表相較於表變量可以存儲稍微大量一些的數據,比如數據量超過10萬條記錄數,數據空間佔用量超過100MB。但是,如果經常有類似的臨時表使用場景時,建議對Tempdb數據庫做性能優化相關的配置工作。

臨時表的使用

爲了和表變量形成對比,我特意將表結構和數據保持一致,不同的地方在於,我們可以對約束進行顯示指定命名,可以創建索引。在次,爲了看清楚局部臨時表和全局臨時表的區別,我們也創建了一個全局臨時表。

USE tempdb
GO
IF OBJECT_ID('tempdb..#tb_table','U') IS NOT NULL
    DROP TABLE #tb_table
GO
CREATE TABLE #tb_table(
RowID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
,ProductName NVARCHAR(50) NOT NULL UNIQUE
,Length DECIMAL(4,2) NOT NULL CHECK(Length>0.0)
,Windth DECIMAL(4,2) NOT NULL
,Height DECIMAL(4,2) NOT NULL CHECK(Height>0.0)
,Dimension AS (Length * Windth * Height)
,Indate DATETIME NOT NULL  CONSTRAINT DF_tbTable DEFAULT(GETDATE())
,CONSTRAINT CK_Windth CHECK(Windth>0.0)
);

CREATE INDEX IX_ProductName
ON #tb_table(ProductName);
GO

INSERT INTO #tb_table(ProductName, Length, Windth, Height) VALUES('A', 0.1, 0.2, 0.3);
INSERT INTO #tb_table(ProductName, Length, Windth, Height) VALUES('B', 0.4, 0.5, 0.6);
INSERT INTO #tb_table(ProductName, Length, Windth, Height) VALUES('C', 0.7, 0.8, 0.9);

IF OBJECT_ID('tempdb..##tb_table','U') IS NOT NULL
    DROP TABLE ##tb_table
GO
SELECT * 
    INTO ##tb_table
FROM #tb_table;

UPDATE A
SET Length = 2.5
FROM #tb_table AS A
WHERE RowID = 1
;

DELETE TOP(1) A
FROM #tb_table AS A
WHERE RowID = 2;

SELECT * FROM #tb_table;

SELECT *
FROM ##tb_table

執行上面的局部臨時表和全局臨時表創建語句之後,我們在SSMS中新開啓一個連接,執行下面的語句:

SELECT *
FROM ##tb_table
GO

SELECT *
FROM #tb_table

返回執行結果如下:
01.png
返回執行消息如下:
02.png
從返回的結果分析可知:局部臨時表僅當前連接可以訪問,對其他進程不可見(訪問報告對象不存在的錯誤),而全局臨時表不僅當前連接可以訪問,對其他進程可見。

寫在最後

關於SQL Server表變量和臨時表的使用規則是一個仁者見仁智者見智的話題,所以我們希望能夠把這個話題儘可能的剖析清楚,讓讀者對兩者有非常清楚的認識。



原文地址——獲取更多精華博文

發佈了35 篇原創文章 · 獲贊 109 · 訪問量 93萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章