預存程序是資料庫邏輯封裝的核心單元。本文聚焦實戰易踩坑:ALTER/CREATE 差異、輸出參數雙重 OUTPUT、EXEC 作用域隔離、動態 SQL 注入防範、效能計畫快取陷阱。
| 差異點 | CREATE | ALTER |
|---|---|---|
| 物件存在性 | 不存在才能建立 | 必須已存在 |
| 權限保留 | 重建後權限遺失 | 保留原有 GRANT/DENY |
| Object ID | 新 ID | 同一 ID,相依物件不失效 |
| 相依關係 | 中斷 | 保留 |
最佳實務: 部署腳本統一用 IF EXISTS ... DROP ... CREATE 或 CREATE OR ALTER(SQL Server 2016 SP1+),避免權限遺失。
-- 定義
CREATE PROC dbo.GetOrderCount @CustomerID INT, @Total INT OUTPUT
AS
SELECT @Total = COUNT(*) FROM Orders WHERE CustomerID = @CustomerID;
GO
-- 呼叫:定義與呼叫「雙方」都要加 OUTPUT
DECLARE @Cnt INT;
EXEC dbo.GetOrderCount @CustomerID = 123, @Total = @Cnt OUTPUT; -- ← 這裡必須加 OUTPUT
PRINT @Cnt;
⚠ 呼叫端漏寫 OUTPUT:不報錯、不回傳值、變數保持 NULL,極難除錯。
EXEC 內的變數外部不可見,外部變數需轉字串拼接才能傳入(或用 sp_executesql 參數化)EXEC 前組裝完成,不可在同一批次中邊組裝邊執行EXECCREATE PROC dbo.SearchOrders @Keyword NVARCHAR(100), @Status TINYINT = NULL
AS
BEGIN
DECLARE @sql NVARCHAR(MAX) = N'
SELECT OrderID, CustomerID, Status, CreatedAt
FROM dbo.Orders
WHERE 1=1';
DECLARE @params NVARCHAR(MAX) = N'@Keyword NVARCHAR(100), @Status TINYINT';
IF @Keyword IS NOT NULL
SET @sql += N' AND (CustomerName LIKE @Keyword + N''%'' OR OrderNo LIKE @Keyword + N''%'')';
IF @Status IS NOT NULL
SET @sql += N' AND Status = @Status';
EXEC sp_executesql @sql, @params, @Keyword, @Status;
END;
✅ sp_executesql 參數化 → 計畫重用 + 防注入 + 型別安全。
| 優點 | 缺點 |
|---|---|
| 效能:計畫快取、減少網路往返 | 平台鎖定:Oracle/PostgreSQL 語法差異大 |
| 安全:權限隔離、防 SQL 注入 | 計畫參數嗅探導致錯誤計畫(參數值變化大時) |
| 可重用:業務邏輯集中、版本控管 | 除錯困難、缺乏單元測試工具鏈 |
| 封裝:隱藏結構、減少表直接暴露 | 版本部署需協調 DBA、應用端 |
OPTION (RECOMPILE):每次重新編譯,適用高變異度、低頻程序OPTION (OPTIMIZE FOR @param = 'typical_value'):指定典型值DECLARE @local = @param; ... WHERE col = @local(優化器視為未知值,走平均密度)結語: 預存程序是利器,但需建立「命名規範、參數化動態 SQL、計畫監控、自動化部署」四大工程化配套,才能發揮最大效益。