首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 游標 完整實戰指南:宣告、開啟、擷取、關閉、釋放與效能替代方案

     編輯:關於SqlServer

前言

游標 提供逐列處理結果集的能力,將集合導向的 SQL 行為轉為逐列程式邏輯。但游標**效能極差、鎖定久發、記憶體壓力大**,應視為**最後手段**。本文完整涵蓋語法、範例、參數選項、常見陷阱與集合導向替代方案。

游標生命週期

  1. DECLARE CURSOR 定義
  2. OPEN 執行查詢、建立結果集
  3. FETCH NEXT INTO @vars 逐列讀取
  4. WHILE @@FETCH_STATUS = 0 迴圈處理
  5. CLOSE 釋放結果集鎖定
  6. DEALLOCATE 釋放游標結構記憶體

完整語法與參數選項


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僅目前 Batch/Proc 可見 (預設)避免名稱衝突
GLOBAL整個連線可見慎用
FORWARD_ONLY僅支援 FETCH NEXT (預設)最輕量、唯讀最佳
SCROLL支援 FIRST/LAST/PRIOR/NEXT/ABSOLUTE/RELATIVE需臨時表支援、較重
STATIC開啟時複製結果集到 tempdb,後續異動不可見一致性快照、允許 SCROLL
KEYSET固定鍵值集合,非鍵值更新可見,插入不可見折衷方案
DYNAMIC即時反映所有異動最重、不支援 ABSOLUTE FETCH
FAST_FORWARDFORWARD_ONLY + READ_ONLY + 內部最佳化 (預設優先)**唯讀單向遍歷首選**
READ_ONLY不可透過游標更新/刪除允許 STATIC/FAST_FORWARD 最佳化

標準範例:將 TableA 資料複製到 TableB


DECLARE @ColA NVARCHAR(50), @ColB NVARCHAR(50), @ColC NVARCHAR(50);

DECLARE Cur CURSOR LOCAL FAST_FORWARD FOR
    SELECT ColA, ColB, ColC FROM TableA;

OPEN Cur;
FETCH NEXT FROM Cur INTO @ColA, @ColB, @ColC;

WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO TableB (ColA, ColB, ColC) VALUES (@ColA, @ColB, @ColC);
    FETCH NEXT FROM Cur INTO @ColA, @ColB, @ColC;
END

CLOSE Cur;
DEALLOCATE Cur;

進階:游標更新/刪除 (WHERE CURRENT OF)


DECLARE Cur CURSOR LOCAL FORWARD_ONLY FOR
    SELECT Id, Status FROM Orders WHERE Status = 'Pending' FOR UPDATE OF Status;

OPEN Cur;
FETCH NEXT FROM Cur INTO @Id, @Status;

WHILE @@FETCH_STATUS = 0
BEGIN
    -- 業務邏輯判斷...
    IF @SomeCondition = 1
        UPDATE Orders SET Status = 'Processing' WHERE CURRENT OF Cur;
    ELSE
        DELETE FROM Orders WHERE CURRENT OF Cur;
    
    FETCH NEXT FROM Cur INTO @Id, @Status;
END
CLOSE Cur; DEALLOCATE Cur;
  • 需宣告 FOR UPDATE;鎖定該列至交易結束。
  • WHERE CURRENT OF 直接定位當前游標列,無需再次比對鍵值。

游標變數 (Cursor Variable) - 參數傳遞


-- 程序回傳游標
CREATE PROC GetOrders @CustId INT, @Cur CURSOR VARYING OUTPUT
AS
    SET @Cur = CURSOR FORWARD_ONLY STATIC FOR SELECT * FROM Orders WHERE CustId = @CustId;
    OPEN @Cur;
GO

-- 呼叫端
DECLARE @MyCur CURSOR, @Id INT, @Amt MONEY;
EXEC GetOrders 123, @Cur = @MyCur OUTPUT;
FETCH NEXT FROM @MyCur INTO @Id, @Amt;
WHILE @@FETCH_STATUS = 0 BEGIN ... FETCH NEXT ... END
CLOSE @MyCur; DEALLOCATE @MyCur;

效能陷阱與替代方案 (必讀)

場景游標寫法集合導向替代 (快 10-1000 倍)
逐列計算/呼叫外部 APIWHILE FETCH ... EXEC sp_ext_api**CLR 整合 / Service Broker / 應用層批次處理**
逐列條件更新WHILE FETCH ... UPDATE ... WHERE CURRENT OF單一 UPDATE 配合 CASE/WHERE:
UPDATE T SET Col = CASE WHEN ... THEN ... ELSE ... END WHERE ...
逐列插入WHILE FETCH ... INSERT ...INSERT ... SELECT ... 或 MERGE
逐列刪除WHILE FETCH ... DELETE WHERE CURRENT OFDELETE FROM T WHERE ...
跨表同步雙層游標巢狀MERGE / INSERT ... SELECT / UPDATE ... JOIN

何時「必須」用游標?

  1. **維護作業**:DBCC、備份/還原、索引重建等系統程序本身使用游標。
  2. **動態 SQL 批次**:需對每個資料庫/表執行不同動態 SQL (如 sp_msforeachdb 內部)。
  3. **舊版相容**:SQL 2000 以前無 MERGE、TRY/CATCH、窗口函數,被迫用游標。
  4. **極少數業務**:確實需要逐列呼叫外部資源 (Web Service、檔案系統) 且無法批次化。

最佳實踐清單

  1. **預設宣告**:LOCAL FAST_FORWARD READ_ONLY (或直接 FAST_FORWARD)。
  2. **盡早 CLOSE/DEALLOCATE**:釋放鎖定與記憶體,避免洩漏。
  3. **檢查 @@FETCH_STATUS**:0=成功, -1=失敗/超出範圍, -2=列已刪除。
  4. **避免巢狀游標**:單層游標已極慢,巢狀更是災難;改用暫存表/表變數中繼。
  5. **程式碼審查強制標註**:凡出現 CURSOR 關鍵字,PR 必須附上「為何不可用集合導向」說明。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved