臨時表是 T-SQL 中處理中間結果的利器,但創建方式、作用域、清理時機若不當,會導致系統表爭用、緩存污染、並發阻塞。本文對比兩種創建語法、演示正確刪除模式、對比表變量,給出生產級最佳實踐。
CREATE TABLE #tmp (
name NVARCHAR(255),
id INT,
idx_name NONCLUSTERED (name) -- 臨時表也可建索引
);
SELECT
COUNT(id) AS storyNum,
SUM(CASE WHEN ISNUMERIC(code)=1 THEN CONVERT(DECIMAL(10,2), code) ELSE 0 END) AS codeNum,
SUM(CASE WHEN ISNUMERIC(realcode)=1 THEN CONVERT(DECIMAL(10,2), realcode) ELSE 0 END) AS realcodeNum,
tdtname, cycle, jiracomponent, jirastatename, qualityvalue, storycodellt
INTO #tmp
FROM IKNOW_STORY_U2000V1R7C00
GROUP BY tdtname, cycle, jiracomponent, jirastatename, qualityvalue, storycodellt;
| 類型 | 前綴 | 作用域 | 生命周期 | 跨會話可見 |
|---|---|---|---|---|
| 局部臨時表 | # | 當前會話 | 會話結束或顯式 DROP 自動清理 | 否 |
| 全局臨時表 | ## | 所有會話 | 創建會話結束且無其他會話引用時清理 | 是 |
| 表變量 | @ | 當前批次/存儲過程 | 批次結束自動清理 | 否 |
錯誤示範:直接 DROP TABLE #tmp 若表不存在會報錯;查 sysobjects 判斷錯誤(臨時表在 tempdb,名稱含後綴隨機串)。
標準安全模式:
-- 單條語句:存在則刪,不存在不報錯
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
DROP TABLE #tmp;
存儲過程結尾統一清理(推薦):
CREATE PROC dbo.usp_Example
AS
BEGIN
SET NOCOUNT ON;
-- 業務邏輯...
CREATE TABLE #stage (...);
INSERT #stage ...;
-- 返回結果
SELECT * FROM #stage;
-- 顯式清理(雖然會話結束自動清,但顯式釋放系統表鎖、減少 tempdb 壓力)
IF OBJECT_ID('tempdb..#stage') IS NOT NULL
DROP TABLE #stage;
END
GO
需要統計信息/索引/大數據量/動態 SQL 引用 → 臨時表 (#tmp)
├─ 數據量 > 1萬行
├─ 需建非聚集索引優化連接/篩選
├─ 需在動態 SQL (EXEC/sp_executesql) 中跨批次訪問
└─ 需並行執行計劃
小數據量/無索引需求/僅當前批次/避免重編譯 → 表變量 (@tmp)
├─ 行數 < 1000(經驗值)
├─ 無統計信息,優化器假設 1 行,適合嵌套循環連接
├─ 不觸發重編譯,存儲過程重用性好
└─ 事務回滾不影響表變量數據(隱式自主事務特性)
-- 場景:從大表導入臨時表,數據量大(百萬行+) -- SELECT INTO 為最小日誌操作(簡單恢復模式/大量日誌模式),極快 SELECT * INTO #big_tmp FROM LargeTable WHERE FilterCond; -- 後續需建索引再查詢 CREATE NONCLUSTERED INDEX IX_#big_tmp_Key ON #big_tmp (KeyCol); -- 小數據量或需精確控制類型:CREATE TABLE + INSERT CREATE TABLE #small_tmp (Col1 INT, Col2 NVARCHAR(50)); INSERT #small_tmp SELECT Col1, Col2 FROM SmallTable;
-- 循環處理中需重置臨時表 -- DELETE 產生大量日誌、鎖升級、觸發器 -- TRUNCATE 只記錄頁釋放、極快、重置自增種子 TRUNCATE TABLE #tmp; -- 臨時表同樣適用 -- 然後再 INSERT 新批次
掌握臨時表的「生、用、死」全生命周期管理,是寫出高性能、高併發 T-SQL 存儲過程的基本功。