游標聲明:兩種語法標準
ANSI SQL-92 標準語法
DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR
FOR select_statement
[FOR {READ ONLY | UPDATE [OF column_list]}]
T-SQL 擴充語法(功能更完整)
DECLARE cursor_name CURSOR
[LOCAL | GLOBAL]
[FORWARD_ONLY | SCROLL]
[STATIC | KEYSET | DYNAMIC | FAST_FORWARD]
[READ_ONLY | SCROLL_LOCKS | OPTIMISTIC]
FOR select_statement
[FOR {READ ONLY | UPDATE [OF column_list]}]
關鍵字選項詳細對照
| 關鍵字 | 說明 |
| INSENSITIVE / STATIC | 建立結果集暫存複本,基表變更不可見,唯讀 |
| SCROLL | 支援 FIRST/LAST/NEXT/PRIOR/ABSOLUTE/RELATIVE 擷取 |
| FORWARD_ONLY | 僅支援 NEXT,效能最佳(預設) |
| KEYSET | 固定鍵值集合,非鍵值欄位變更可見,鍵值變更/新增不可見 |
| DYNAMIC | 反映所有基表變更,開銷最大 |
| FAST_FORWARD | FORWARD_ONLY + READ_ONLY 最佳化,內部自動選擇 STATIC/DYNAMIC |
| READ_ONLY | 禁止透過游標修改資料 |
| SCROLL_LOCKS | 鎖定擷取列確保更新/刪除成功 |
| OPTIMISTIC | 樂觀並行,依時間戳/校驗和檢測衝突,失敗則報錯 |
| LOCAL | 僅在宣告批次/程序內可見(預設) |
| GLOBAL | 連接存續期間全域可見,連接結束才釋放 |
自動轉換規則(重要)
- SELECT 含 `DISTINCT`/`UNION`/`GROUP BY`/彙總函數 → 強制 STATIC
- 基表無唯一索引 → 強制 STATIC
- `ORDER BY` 欄位非唯一識別碼 → DYNAMIC 降級為 KEYSET,再失敗則 STATIC
- ANSI-92 語法無 `INSENSITIVE` 關鍵字,行為相同
開啟游標
OPEN { { [GLOBAL] cursor_name } | cursor_variable_name }
- 執行查詢、建立結果集、定位游標于第一列之前
- `@@CURSOR_ROWS` 回傳列數:負值表示非同步填充中,絕對值為目前列數
擷取資料
FETCH [NEXT | PRIOR | FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}]
FROM { [GLOBAL] cursor_name } | cursor_variable_name }
- SCROLL 游標支援所有選項;FORWARD_ONLY 僅支援 NEXT
- 可擷取至區域變數:`FETCH NEXT FROM cur INTO @var1, @var2`
修改/刪除與關閉
UPDATE table SET col = val WHERE CURRENT OF cursor_name
DELETE FROM table WHERE CURRENT OF cursor_name
CLOSE cursor_name
DEALLOCATE cursor_name
- `CLOSE` 釋放結果集鎖定,保留游標定義,可重新 `OPEN`
- `DEALLOCATE` 徹底釋放資源,GLOBAL 游標需顯式釋放
最佳實務建議
- 能用集合式查詢絕不用游標 — 效能差異可達 10~100 倍
- 必須用游標時:優先 `FAST_FORWARD` / `FORWARD_ONLY READ_ONLY`
- 避免 `DYNAMIC` / `SCROLL_LOCKS` 除非極度必要
- 盡量用 `LOCAL` 變數游標,避免 `GLOBAL` 洩漏資源
- 迴圈中務必檢查 `@@FETCH_STATUS = 0`
- 用畢立即 `CLOSE` + `DEALLOCATE`