前言
T-SQL 提供完整的程式控制流程結構,支援條件分支、迴圈、跳轉、等待、錯誤處理與條件運算。本文系統整理各陳述式語法、範例與避坑指南。
一、IF ... ELSE 條件分支
IF @Score >= 60
PRINT N'及格';
ELSE
PRINT N'不及格';
-- 多陳述式需用 BEGIN...END 區塊
IF @Score >= 90
BEGIN
PRINT N'優等';
INSERT INTO HonorRoll (StudentId) VALUES (@StudentId);
END
ELSE IF @Score >= 60
PRINT N'及格';
ELSE
PRINT N'不及格';
- 條件必須為布林運算式 (回傳 TRUE/FALSE/UNKNOWN)。
UNKNOWN (含 NULL 比較) 視同 FALSE,進入 ELSE 區塊。
二、WHILE 迴圈
DECLARE @i INT = 1;
WHILE @i <= 10
BEGIN
PRINT @i;
SET @i += 1;
-- 提前跳出
IF @i = 5 BREAK;
-- 跳過本次迴圈
IF @i = 3 CONTINUE;
END
- 條件為 TRUE 時持續執行;FALSE 或 UNKNOWN 結束。
BREAK 跳出迴圈;CONTINUE 跳至下一次迭代判斷。
- **避免無限迴圈**:確保迴圈內修改條件變數。
三、GOTO 無條件跳轉
DECLARE @Retry INT = 0;
RetryLabel:
SET @Retry += 1;
IF @Retry > 3 GOTO ExitLabel;
-- 業務邏輯...
IF @@ERROR <> 0 GOTO RetryLabel;
ExitLabel:
PRINT N'結束';
- 標籤格式:
LabelName: (冒號結尾)。
- **慎用**:破壞結構化程式設計,難以維護;優先考慮 WHILE/TRY-CATCH 替代。
- 無法跨批次 (GO) 跳轉。
四、WAITFOR 延遲/排程
-- 等待 10 秒
WAITFOR DELAY '00:00:10';
-- 等待至指定時間 (若已過則下一天)
WAITFOR TIME '23:59:59';
-- 結合迴圈輪詢
WHILE (SELECT COUNT(*) FROM JobQueue) = 0
WAITFOR DELAY '00:00:05'; -- 每 5 秒檢查一次
DELAY 最大 23:59:59;TIME 為 24 小時制。
- 會阻塞執行緒,**慎用於高併發核心路徑**;輪詢建議改用 Service Broker / SignalR / 背景工作。
五、TRY ... CATCH 錯誤處理 (SQL 2005+)
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO Orders (...) VALUES (...);
INSERT INTO OrderItems (...) VALUES (...);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
-- 取得錯誤資訊
DECLARE @ErrMsg NVARCHAR(4000) = ERROR_MESSAGE();
DECLARE @ErrSeverity INT = ERROR_SEVERITY();
DECLARE @ErrState INT = ERROR_STATE();
DECLARE @ErrLine INT = ERROR_LINE();
DECLARE @ErrProc NVARCHAR(200) = ERROR_PROCEDURE();
-- 記錄錯誤日誌
INSERT INTO ErrorLog (Message, Severity, State, ProcedureName, LineNumber, LogTime)
VALUES (@ErrMsg, @ErrSeverity, @ErrState, @ErrProc, @ErrLine, SYSDATETIME());
-- 重新拋出 (SQL 2012+ THROW; 舊版 RAISERROR)
THROW; -- 保留原始堆疊與狀態
END CATCH
- 捕捉嚴重度 11-19 的執行階段錯誤;嚴重度 20+ (系統嚴重錯誤) 會中斷連線,無法捕捉。
THROW (2012+) 比 RAISERROR 更簡潔、保留原始錯誤號碼/狀態。
- 交易計數
@@TRANCOUNT 必檢,避免重複 ROLLBACK 報錯。
六、CASE 條件運算式 (兩種形式)
6.1 簡單 CASE (Simple CASE) —— 等值比對
DECLARE @Grade CHAR(1) = 'B';
SELECT
CASE @Grade
WHEN 'A' THEN N'優秀'
WHEN 'B' THEN N'良好'
WHEN 'C' THEN N'普通'
ELSE N'需加強'
END AS Result;
-- 回傳: 良好
- 輸入運算式 (
@Grade) 與各 WHEN 運算式做**等值比較**。
- 型別需相容或可隱含轉換。
6.2 搜尋 CASE (Searched CASE) —— 布林運算式
DECLARE @Score INT = 85;
SELECT
CASE
WHEN @Score >= 90 THEN N'優等'
WHEN @Score >= 60 THEN N'及格'
ELSE N'不及格'
END AS Result;
-- 回傳: 及格
- **無輸入運算式**;每個
WHEN 為獨立布林運算式,支援範圍、複合條件。
- **依序評估**,第一個 TRUE 的 THEN 回傳,後續略過。
6.3 CASE 關鍵差異與注意點
| 特性 | 簡單 CASE | 搜尋 CASE |
| 輸入運算式 | 有 | 無 |
| WHEN 形式 | 運算式 (等值比對) | 布林運算式 |
| 適用場景 | 離散值映射 (狀態碼、類別) | 範圍判斷、複合條件 |
| ELSE 省略 | 回傳 NULL | 回傳 NULL |
6.4 進階用法:聚合/排序/分組中使用 CASE
-- 條件聚合
SELECT
SUM(CASE WHEN Sex = 'M' THEN 1 ELSE 0 END) AS MaleCount,
SUM(CASE WHEN Sex = 'F' THEN 1 ELSE 0 END) AS FemaleCount
FROM Students;
-- 動態排序
ORDER BY
CASE @SortBy WHEN 'Name' THEN Name WHEN 'Age' THEN CAST(Age AS VARCHAR(10)) END;
-- 動態分組
GROUP BY CASE WHEN Age < 18 THEN 'Minor' ELSE 'Adult' END;
七、程式碼區塊:BEGIN ... END
- 將多條陳述式視為單一陳述式,用於 IF/ELSE/WHILE/TRY/CATCH 內。
- 可巢狀;建議縮排對齊提升可讀性。
八、總結:控制流程選型指南
| 需求 | 推薦結構 |
| 單一條件分支 | IF ... ELSE |
| 多重互斥條件 | IF ... ELSE IF ... ELSE |
| 離散值映射/轉換 | 簡單 CASE |
| 範圍/複合條件判斷 | 搜尋 CASE |
| 重複執行直到條件變化 | WHILE |
| 錯誤處理/交易保護 | TRY ... CATCH |
| 定時/延遲執行 | WAITFOR |
| 極少數跳轉需求 | GOTO (最後手段) |