預存程序 (Stored Procedure, SP) 是封裝複雜業務邏輯、減少網路往返、提升安全性的核心工具。但撰寫不當會導致維護困難、效能低落。本文結合實戰經驗,從編碼規範、查詢優化、索引策略、tempdb 使用、事務管理五大維度,提供可落地的優化準則。
避免直接使用 Database.dbo.Table 三段式命名,因 sp_depends 無法追蹤跨庫相依性,影響影響分析。在當前資料庫建立 View 映射外部表,SP 僅引用 View。
提交前必須執行 SET SHOWPLAN_ON ON 或使用 SSMS 「顯示預估執行計畫」,檢查:
usp_GetCustomerOrders@ 前綴,區域變數加 @,全域暫存表 ##,區域暫存表 #WHILE @@FETCH_STATUS = 0 + 區域暫存表,或 CLR 彙總。LOCAL FAST_FORWARD READ_ONLY (唯讀、單向、低開銷)。| 原則 | 錯誤寫法 | 正確寫法 | 說明 |
|---|---|---|---|
| 欄位不參與運算 | WHERE YEAR(OrderDate) = 2023 | WHERE OrderDate >= '2023-01-01' AND OrderDate < '2024-01-01' | 保留 SARGable,可用索引 Seek |
| 條件順序遵循索引鍵序 | WHERE ColB = 1 AND ColA = 2 (索引為 A,B) | WHERE ColA = 2 AND ColB = 1 | 符合最左前綴原則 |
| 範圍條件放最後 | WHERE ColA > 100 AND ColB = 5 | WHERE ColB = 5 AND ColA > 100 | 等值優先,範圍次之 |
| EXISTS 替代 COUNT | IF (SELECT COUNT(*) FROM T WHERE ...) > 0 | IF EXISTS (SELECT 1 FROM T WHERE ...) | EXISTS 遇第一筆即停止 |
| UNION ALL 替代 UNION | SELECT ... UNION SELECT ... | SELECT ... UNION ALL SELECT ... | UNION 需排序去重,ALL 直接合併 |
| 場景 | 建議做法 | 避免做法 |
|---|---|---|
| 大量中繼資料 | SELECT INTO #Temp (最小記錄,無鎖系統表) | CREATE TABLE #Temp 後 INSERT (大量時產生大量 Log) |
| 小量中繼資料 | CREATE TABLE #Temp 後 INSERT (緩解系統表爭用) | SELECT INTO (頻繁建刪系統表鎖定) |
| 暫存表需索引 | 建立暫存表 → 建索引 → 插入資料,或放入獨立子 SP (確保重編譯使用新索引) | 建索引後才插入大量資料 (統計資料不準) |
| 結束清理 | TRUNCATE TABLE #Temp → DROP TABLE #Temp (釋放系統表鎖) | 僅 DROP 或自然結束 |
| 排序/分組/去重 | 評估是否可在來源端完成,或加索引避免 TempDB 溢寫 | 大量 DISTINCT/ORDER BY/GROUP BY 直接在 SP 內執行 |
DECLARE @local = @param; ... WHERE col = @local (優化器視為未知值,產生平均計畫)。sp_executesql 參數化,每組參數產生專屬計畫 (防止計畫膨脹)。CREATE PROCEDURE usp_Example
@Param INT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON; -- 任何錯誤自動回滾
BEGIN TRY
BEGIN TRANSACTION;
-- 核心業務邏輯
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
-- 記錄錯誤至 Log 表
INSERT INTO dbo.ErrorLog (ProcName, ErrorNumber, ErrorMessage, ErrorTime)
VALUES (OBJECT_NAME(@@PROCID), ERROR_NUMBER(), ERROR_MESSAGE(), SYSDATETIME());
-- 重新拋出或返回自定義代碼
THROW; -- SQL 2012+; 舊版用 RAISERROR
END CATCH
END
高效能預存程序的關鍵在於:集合導向思維、SARGable 查詢、精準索引、tempdb 節制、事務最小化、參數嗅探管控。建立團隊編碼規範、Code Review 機制、上線前執行計畫審查、事後慢查詢追蹤,才能持續交付高品質的資料庫程式碼。