T-SQL 提供完整的程式流程控制能力,支援條件判斷、迴圈、區塊結構,是撰寫預存程序、觸發器、批次腳本的基礎。
-- 基本語法
IF <條件運算式>
<陳述式或區塊>
[ELSE
<陳述式或區塊>]
-- 範例:薪資等級判斷
DECLARE @Salary MONEY = 55000;
IF @Salary > 50000
PRINT '高薪資等級';
ELSE IF @Salary > 30000
PRINT '中薪資等級';
ELSE
PRINT '初級薪資等級';
CREATE PROCEDURE dbo.usp_GetOrders
@CustomerID NCHAR(5) = NULL,
@OrderDateFrom DATE = NULL,
@OrderDateTo DATE = NULL
AS
BEGIN
DECLARE @SQL NVARCHAR(MAX) = N'SELECT * FROM dbo.Orders WHERE 1=1';
IF @CustomerID IS NOT NULL
SET @SQL += N' AND CustomerID = @CustID';
IF @OrderDateFrom IS NOT NULL
SET @SQL += N' AND OrderDate >= @DateFrom';
IF @OrderDateTo IS NOT NULL
SET @SQL += N' AND OrderDate <= @DateTo';
EXEC sp_executesql @SQL,
N'@CustID NCHAR(5), @DateFrom DATE, @DateTo DATE',
@CustID = @CustomerID, @DateFrom = @OrderDateFrom, @DateTo = @OrderDateTo;
END;
BEGIN
-- 區域變數宣告 (僅在區塊內有效)
DECLARE @LocalVar INT = 10;
-- 多行陳述式
SET @LocalVar = @LocalVar * 2;
PRINT @LocalVar;
-- 巢狀區塊
BEGIN
DECLARE @InnerVar INT = 5;
PRINT @InnerVar;
END
-- @InnerVar 在此處不可見
END
CASE <輸入運算式>
WHEN <比較值1> THEN <結果1>
WHEN <比較值2> THEN <結果2>
...
[ELSE <預設結果>]
END
-- 範例:州代碼轉全名
SELECT
State,
CASE State
WHEN 'CA' THEN 'California'
WHEN 'NY' THEN 'New York'
WHEN 'TX' THEN 'Texas'
ELSE 'Other State'
END AS StateName
FROM Customers;
CASE
WHEN <布林運算式1> THEN <結果1>
WHEN <布林運算式2> THEN <結果2>
...
[ELSE <預設結果>]
END
-- 範例:薪資區間分級
SELECT
EmployeeName,
Salary,
CASE
WHEN Salary >= 100000 THEN 'Executive'
WHEN Salary >= 70000 THEN 'Senior'
WHEN Salary >= 40000 THEN 'Mid-Level'
ELSE 'Junior'
END AS Level
FROM Employees;
SUM(CASE WHEN Status = 'Active' THEN 1 ELSE 0 END)ORDER BY CASE @SortCol WHEN 'Name' THEN Name WHEN 'Date' THEN HireDate ENDCASE WHEN Divisor = 0 THEN NULL ELSE Dividend / Divisor ENDWHILE <布林條件>
BEGIN
<陳述式區塊>
[BREAK;] -- 立即跳出迴圈
[CONTINUE;] -- 跳過本次剩餘陳述式,進入下一次迭代
END
DECLARE @N INT = 5, @Factorial BIGINT = 1, @Counter INT = 1;
WHILE @Counter <= @N
BEGIN
SET @Factorial = @Factorial * @Counter;
SET @Counter += 1;
-- 提前結束示範
IF @Factorial > 100
BREAK;
END
PRINT @Factorial; -- 120
DECLARE @i INT = 1, @j INT;
WHILE @i <= 3
BEGIN
SET @j = 1;
WHILE @j <= 3
BEGIN
IF @i = 2 AND @j = 2
BREAK; -- 只跳出內層迴圈,外層繼續
PRINT CONCAT('i=', @i, ' j=', @j);
SET @j += 1;
END
SET @i += 1;
END
-- 輸出: i=1 j=1, i=1 j=2, i=1 j=3, i=2 j=1, i=3 j=1, i=3 j=2, i=3 j=3
DECLARE @Name NVARCHAR(100);
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT Name FROM dbo.Products WHERE Price > 100;
OPEN cur;
FETCH NEXT FROM cur INTO @Name;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @Name;
FETCH NEXT FROM cur INTO @Name;
END
CLOSE cur;
DEALLOCATE cur;
DECLARE @Counter INT = 0;
StartLoop:
SET @Counter += 1;
PRINT @Counter;
IF @Counter < 5 GOTO StartLoop;
-- 輸出 1 到 5
CREATE PROCEDURE dbo.usp_ValidateOrder @OrderID INT
AS
BEGIN
IF NOT EXISTS (SELECT 1 FROM Orders WHERE OrderID = @OrderID)
BEGIN
RAISERROR('訂單不存在', 16, 1);
RETURN -1; -- 立即結束程序,回傳狀態碼
END
-- 後續邏輯...
RETURN 0;
END;
BEGIN TRY
-- 可能失敗的操作
INSERT INTO Orders ...;
END TRY
BEGIN CATCH
-- 記錄錯誤
INSERT INTO ErrorLog ...;
-- 重新拋出原始例外
THROW;
END CATCH;
| 建議 | 說明 |
|---|---|
| 避免游標,改用集合操作 | SET-based 操作通常比逐列處理快 10-100 倍 |
| WHILE 迴圈內避免 SELECT * FROM 大表 | 改用索引查詢、暫存表、表變數減少 I/O |
| CASE 運算式優於多個 IF/ELSE | 查詢最佳化器可產生更有效執行計畫 |
| 使用 sp_executesql 替代 EXEC(@sql) | 參數化、計畫重用、防注入 |
| BEGIN TRY/CATCH 取代 @@ERROR 檢查 | 結構化錯誤處理、支援嵌套、THROW 保留堆疊 |
| 變數宣告盡量靠近使用位置 | 減少作用域、提高可讀性、避免重複宣告錯誤 |