TRY...CATCH 是 SQL Server 2005 引入的結構化錯誤處理機制,極大提升了 T-SQL 的健壯性與可維護性,其語法風格與 C#、Java 等主流語言一脈相承,降低了開發者的認知負擔。
在 2000 版本中,每執行一條 DML 語句後必須立即檢查 @@ERROR 全域變數,因為它會被下一條語句覆蓋。典型的轉帳存儲過程充滿了巢狀 IF @@ERROR != 0 判斷與 ROLLBACK/RETURN,代碼冗長且極易遺漏檢查點:
CREATE PROC usp_AccountTransaction @AccountNum INT, @Amount DECIMAL AS BEGIN BEGIN TRANSACTION UPDATE MyChecking SET Amount = Amount - @Amount WHERE AccountNum = @AccountNum IF @@ERROR != 0 BEGIN ROLLBACK TRANSACTION; RETURN END UPDATE MySavings SET Amount = Amount + @Amount WHERE AccountNum = @AccountNum IF @@ERROR != 0 BEGIN ROLLBACK TRANSACTION; RETURN END COMMIT TRANSACTION END
BEGIN TRY -- 可能拋出錯誤的陳述式 END TRY BEGIN CATCH -- 錯誤處理邏輯:記錄、回滾、重拋等 END CATCH
| 函數 | 說明 |
|---|---|
| ERROR_NUMBER() | 錯誤號碼 |
| ERROR_SEVERITY() | 嚴重等級 |
| ERROR_STATE() | 狀態碼 |
| ERROR_PROCEDURE() | 發生錯誤的預存程序名稱 |
| ERROR_LINE() | 錯誤行號 |
| ERROR_MESSAGE() | 完整錯誤訊息文字 |
關鍵限制:以上函數僅在 CATCH 區塊內有效,若在外部呼叫將回傳 NULL。
BEGIN TRY SELECT GETDATE(); SELECT 1/0; -- 故意觸發除零錯誤 END TRY BEGIN CATCH SELECT '發生錯誤:' + ERROR_MESSAGE(); RETURN; END CATCH;
ALTER PROC usp_AccountTransaction @AccountNum INT, @Amount DECIMAL AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
UPDATE MyChecking SET Amount = Amount - @Amount WHERE AccountNum = @AccountNum;
UPDATE MySavings SET Amount = Amount + @Amount WHERE AccountNum = @AccountNum;
COMMIT TRAN; -- 成功提交
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRAN; -- 回滾未提交事務
-- 重新拋出原始錯誤給上層呼叫者
RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1);
END CATCH
END
CREATE TABLE ErrorLog ( ErrNum INT, ErrSev NVARCHAR(1000), ErrState INT, ErrProc NVARCHAR(1000), ErrLine INT, ErrMsg NVARCHAR(2000) );
CREATE PROCEDURE usp_LogError AS
BEGIN
INSERT INTO ErrorLog
VALUES (ERROR_NUMBER(), ERROR_SEVERITY(), ERROR_STATE(),
ERROR_PROCEDURE(), ERROR_LINE(), ERROR_MESSAGE());
END
ALTER PROCEDURE dbo.getTodayBirthday AS
BEGIN TRY
DECLARE @today DATETIME = GETDATE();
DECLARE @day VARCHAR(2) = REPLACE(DAY(@today), '0', '');
DECLARE @month VARCHAR(2) = REPLACE(MONTH(@today), '0', '');
DECLARE @year VARCHAR(4) = YEAR(@today);
SELECT * FROM dbo.UserInfo
WHERE REPLACE(DAY(CONVERT(DATETIME, Birthday)), '0', '') = @day
AND REPLACE(MONTH(CONVERT(DATETIME, Birthday)), '0', '') = @month
AND Birthday IS NOT NULL;
END TRY
BEGIN CATCH
EXEC usp_LogError; -- 記錄完整錯誤上下文
END CATCH
TRY...CATCH 無法捕捉 編譯錯誤(語法錯誤)、連線中斷、或嚴重度 > 20 的系統錯誤。TRY...CATCH 時,錯誤會冒泡到最近的外層 CATCH。XACT_ABORT ON 可確保嚴重錯誤自動回滾事務,進一步簡化代碼。掌握 TRY...CATCH 讓 T-SQL 具備現代語言級的錯誤處理能力,是每位 SQL Server 開發者的必修課。