自訂函數 (UDF) 與預存程序 是 T-SQL 封裝邏輯的兩大工具,但設計哲學與能力邊界截然不同。本文從回傳值、副作用限制、參數模式、適用場景四大維度深度比較,並附完整預存程序實戰範例 (參數傳遞、加密、動態 SQL、通配符、條件邏輯)。
| 特性 | 自訂函數 (UDF) | 預存程序 |
|---|---|---|
| 回傳值 | 必須回傳:標量值 或 資料表變數 | 可回傳:標量值 (RETURN)、輸出參數 (OUTPUT)、結果集 (SELECT)、無回傳 |
| OUTPUT 參數 | 不支援 | 支援 |
| 臨時表 | 不支援 (#table) | 支援 |
| 副作用 | 禁止:不得修改資料庫狀態 (INSERT/UPDATE/DELETE/DDL) | 允許:完整 DML/DDL 權限 |
| 結果集 | 不支援直接 SELECT 回傳給用戶端 | 支援多結果集 |
| 錯誤處理 | 有限 (TRY/CATCH 受限) | 完整 TRY/CATCH、THROW、RAISERROR |
| 動態 SQL | 不支援 EXEC/sp_executesql | 支援 |
| 呼叫方式 | SELECT 子句、WHERE、CHECK 約束、計算欄位 | EXEC/EXECUTE 獨立陳述式 |
| 巢狀呼叫 | 函數可呼叫函數,不可呼叫預存程序 | 可呼叫函數與預存程序 |
SELECT dbo.fn_CalcTax(Amount))、做為約束條件。CREATE PROCEDURE sp_query_bankMoney
AS
SELECT * FROM bankMoney;
GO
EXEC sp_query_bankMoney;
CREATE PROCEDURE insert_bank
@param1 CHAR(10), @param2 VARCHAR(20), @param3 VARCHAR(20),
@param4 INT, @param5 INT OUTPUT
WITH ENCRYPTION
AS
INSERT INTO bankMoney (id, userID, sex, Money) VALUES (@param1, @param2, @param3, @param4);
SELECT @param5 = SUM(Money) FROM bankMoney WHERE userID = 'Zhangsan';
GO
-- 呼叫
DECLARE @total_price INT;
EXEC insert_bank '004', 'Zhangsan', '男', 100, @total_price OUTPUT;
PRINT '總餘額為' + CAST(@total_price AS VARCHAR);
CREATE PROCEDURE au_info_all
AS
SELECT au_lname, au_fname, title, pub_name
FROM authors a
INNER JOIN titleauthor ta ON a.au_id = ta.au_id
INNER JOIN titles t ON t.title_id = ta.title_id
INNER JOIN publishers p ON t.pub_id = p.pub_id;
GO
CREATE PROCEDURE au_info @lastname VARCHAR(40), @firstname VARCHAR(20)
AS
SELECT ... FROM ... WHERE au_fname = @firstname AND au_lname = @lastname;
GO
-- 呼叫方式皆合法
EXEC au_info 'Dull', 'Ann';
EXEC au_info @lastname='Dull', @firstname='Ann';
EXEC au_info @firstname='Ann', @lastname='Dull';
CREATE PROCEDURE au_info2 @lastname VARCHAR(30) = 'D%', @firstname VARCHAR(18) = '%'
AS
SELECT ... WHERE au_fname LIKE @firstname AND au_lname LIKE @lastname;
GO
EXEC au_info2; -- 預設 D% 和 %
EXEC au_info2 'Wh%'; -- 位置對應
EXEC au_info2 @firstname = 'A%'; -- 命名參數
CREATE PROCEDURE spUpdateStudent
@fCase INT, @fName NVARCHAR(10), @fAge SMALLINT, @fDiqu VARCHAR(50), @fTel INT
AS
UPDATE Student SET
fAge = @fAge,
fDiqu = CASE WHEN @fCase IN (2,3) THEN @fDiqu ELSE fDiqu END,
fTel = CASE WHEN @fCase = 3 THEN @fTel ELSE fTel END
WHERE fName = @fName;
SELECT * FROM Student;
GO
| 機制 | 語法 | 接收端 | 適用場景 |
|---|---|---|---|
| RETURN | RETURN @intVal | 呼叫端變數 @ret = EXEC sp | 狀態碼 (0=成功, 非0=錯誤代碼) |
| OUTPUT 參數 | @param INT OUTPUT | 呼叫端 @val OUTPUT | 單一/多值資料回傳 |
| 結果集 | SELECT ... | 用戶端/應用程式 | 資料集查詢、報表 |
usp_ 或 sp_ (系統程序前綴) 前綴區分。清晰劃分 UDF 與預存程序的職責邊界,能建構出可維護、高效能、符合資料庫封裝最佳實務的 T-SQL 程式碼架構。