前言
游標提供逐列處理結果集的能力,雖集合式操作通常效能更優,但在複雜邏輯、動態 SQL、維護腳本、逐列呼叫外部程序等場景仍不可或缺。本文系統整理游標組成、三大分類、五階段生命週期、DECLARE 參數詳解與完整實戰範例。
一、 游標組成
- 游標結果集: 定義游標的 SELECT 語句所返回的行集合。
- 游標位置: 指向結果集中特定行的當前指標。
二、 三大分類
| 類型 | 執行位置 | 適用場景 | 支援功能 |
| API 伺服器游標 | 伺服器端 | ODBC/OLE DB/ADO 應用程式 | 靜態、動態、只進、鍵集驅動 |
| Transact-SQL 游標 | 伺服器端 | T-SQL 腳本、預存程序、觸發器 | DECLARE CURSOR 語法定義 |
| 用戶端游標 | 用戶端 | 小結果集、離線處理 | 僅支援只進、靜態;結果集快取於用戶端 |
三、 API 伺服器游標四種模式特性
- 靜態: 開啟時將結果集複製至 tempdb。只讀、不反映後續異動、可見刪除、不可見新增/更新。適合報表/快照。
- 動態: 捲動時即時反映所有變更 (資料值、順序、成員)。開銷最大,適合高併發即時瀏覽。
- 只進: 僅支援 `FETCH NEXT` 循序讀取。可反映變更。開銷最小,適合單向流式處理。
- 鍵集驅動: 開啟時固定成員與順序 (鍵值存 tempdb)。非鍵值更新可見、新增不可見、刪除可見。折衷方案。
四、 T-SQL 游標生命週期五階段
- DECLARE: 定義 SELECT、設定屬性 (SCROLL/FORWARD_ONLY, STATIC/KEYSET/DYNAMIC, READ_ONLY/SCROLL_LOCKS/OPTIMISTIC, LOCAL/GLOBAL)。不取資料。
- OPEN: 執行 SELECT 填充結果集,位置指向第一行前。
- FETCH: 取得當前/指定行,將欄位值賦予變數。`FETCH [NEXT|PRIOR|FIRST|LAST|ABSOLUTE n|RELATIVE n] FROM cursor INTO @var1, @var2...`。
- CLOSE: 釋放結果集鎖定資源,游標結構保留,可再次 OPEN。
- DEALLOCATE: 徹底釋放游標定義與資源。GLOBAL 游標需顯式 DEALLOCATE 或連線斷開隱式釋放。
五、 DECLARE 參數完整對照
DECLARE cursor_name CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
[ TYPE_WARNING ]
FOR select_statement
[ FOR UPDATE [ OF column_list ] ]
| 選項 | 說明 |
| LOCAL/GLOBAL | 作用域:批次/程序 vs 連線層級 |
| FORWARD_ONLY/SCROLL | 只進 vs 可雙向捲動 (預設 FORWARD_ONLY) |
| STATIC/KEYSET/DYNAMIC | 靜態/鍵集/動態 (預設 DYNAMIC) |
| READ_ONLY/SCROLL_LOCKS/OPTIMISTIC | 只讀/鎖定行/樂觀併發 (檢查時間戳/校驗和) |
| FOR UPDATE | 宣告可更新游標,允許 `WHERE CURRENT OF` 更新/刪除 |
六、 完整實戰範例
DECLARE @GoodsCode VARCHAR(20), @GoodsName NVARCHAR(20);
DECLARE MyCursor CURSOR SCROLL
FOR SELECT GoodsCode, GoodsName FROM Master_Goods ORDER BY GoodsID;
OPEN MyCursor;
FETCH NEXT FROM MyCursor INTO @GoodsCode, @GoodsName;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @GoodsCode + ':' + @GoodsName;
FETCH NEXT FROM MyCursor INTO @GoodsCode, @GoodsName;
END
CLOSE MyCursor;
DEALLOCATE MyCursor;
七、 游標定位更新/刪除
UPDATE Master_Goods SET GoodsName = 'NewName' WHERE CURRENT OF MyCursor;
DELETE FROM Master_Goods WHERE CURRENT OF MyCursor;
八、 關鍵系統函數
- `@@FETCH_STATUS`:0=成功, -1=失敗/超出範圍, -2=行已刪除。
- `@@CURSOR_ROWS`:最後開啟游標的合格行數 (動態游標為 -1)。
九、 效能建議
- 優先使用集合式操作 (JOIN、窗口函數、CTE)。
- 必須用游標時:選 `FAST_FORWARD` (只進+唯讀,內部優化)、`LOCAL`、盡早 `CLOSE/DEALLOCATE`。
- 大量資料考慮先導入暫存表,再對暫存表開游標 (減少鎖持有時間)。