關聯式資料庫以集合為導向,單一陳述式處理整個結果集。但應用層常需逐列處理:呼叫外部 API、依前一列狀態決定邏輯、動態 SQL 迭代。游標提供「結果集指標」,讓 T-SQL 能像處理平面檔案一樣逐行讀寫,橋接集合與程式設計的差異。
| 類型 | 定義位置 | 管理端 | 支援功能 | 典型用途 |
|---|---|---|---|---|
| Transact-SQL 游標 | T-SQL 腳本/預存程序/觸發器 | 伺服器 | 僅單列 FETCH,不支援區塊提取 | 伺服器端批次處理、維護腳本 |
| API 游標 | 用戶端程式 (ODBC/OLE DB/DB-Library) | 伺服器 | 支援區塊提取、非同步、多種游標模型 | 傳統桌面應用、ODBC 應用 |
| 用戶端游標 | 用戶端快取完整結果集 | 用戶端 | 僅靜態游標、不支援動態更新 | 小結果集、離線操作、輔助伺服器游標 |
本文聚焦伺服器端 Transact-SQL 游標。
-- 1. 宣告
DECLARE cur_name CURSOR [LOCAL | GLOBAL]
[FORWARD_ONLY | SCROLL]
[STATIC | KEYSET | DYNAMIC | FAST_FORWARD]
[READ_ONLY | SCROLL_LOCKS | OPTIMISTIC]
FOR select_statement;
-- 2. 開啟
OPEN cur_name;
-- 3. 提取 (迴圈)
FETCH [NEXT | PRIOR | FIRST | LAST | ABSOLUTE n | RELATIVE n]
FROM cur_name
INTO @var1, @var2, ...;
-- 4. 關閉與釋放
CLOSE cur_name;
DEALLOCATE cur_name;
tempdb,快照隔離、不反映後續變更、不可更新。FORWARD_ONLY + READ_ONLY 內部最佳化,**唯讀單向掃描首選**。READ_ONLY 明確不可更新,效能最佳。DECLARE @id INT, @name NVARCHAR(100), @cnt INT = 0;
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT id, name FROM dbo.BigTable WHERE status = 0;
OPEN cur;
FETCH NEXT FROM cur INTO @id, @name;
WHILE @@FETCH_STATUS = 0
BEGIN
-- 業務邏輯:呼叫外部、動態 SQL、條件更新
EXEC usp_ProcessItem @id, @name;
SET @cnt += 1;
IF @cnt % 1000 = 0 PRINT 'Processed ' + CAST(@cnt AS VARCHAR(10)) + ' rows';
FETCH NEXT FROM cur INTO @id, @name;
END
CLOSE cur;
DEALLOCATE cur;
UPDATE ... FROM ... JOIN、MERGE、CASE 條件更新、CROSS APPLY 等通常快 10~100 倍。LOCAL FAST_FORWARD READ_ONLY,最小化鎖定與 tempdb 壓力。WHILE 迴圈 + TOP (n) 分批處理、SSIS、Table-Valued Parameters、BULK INSERT。sys.dm_exec_cursors、tempdb 爭用、鎖等待時間。| 現象 | 原因 | 修正 |
|---|---|---|
| 游標結果為空但不報錯 | @@FETCH_STATUS 初值為 -1 | 先 FETCH NEXT 再進 WHILE |
| 巢狀游標外層提前結束 | 內層 DEALLOCATE 釋放同名全域游標 | 一律使用 LOCAL、命名唯一 |
| 更新游標資料不一致 | STATIC 快照不反映自身更新 | 改用 KEYSET 或 DYNAMIC + SCROLL_LOCKS |
| 記憶體洩漏 | 未 CLOSE/DEALLOCATE,連線池重用時殘留 | TRY...CATCH 區塊中 FINALLY 釋放 |
游標是 T-SQL 工具箱中的「手術刀」——精準、靈活,但切片成本高。九成以上逐列需求可改寫為集合操作;剩餘一成請嚴選 FAST_FORWARD READ_ONLY LOCAL,並做好資源釋放與監控。掌握「何時用、如何用、怎麼優化」,是資料庫開發者進階的關鍵門檻。