關聯式資料庫以集合操作為核心(SET-based),但某些業務邏輯需要逐列處理(Row-by-row,RBAR)。游標就是橋樑:將結果集轉化為可逐列迴圈處理的資料結構。
OriginSalary 表:員工薪資 (O_ID, O_Name, O_Salary)
AddSalary 表:加薪額 (O_ID, A_Salary)
需求:O_Salary = O_Salary + A_Salary,逐筆更新 OriginSalary
DECLARE cursor_name CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR select_statement
[ FOR UPDATE [ OF column_list ] ]
| 選項 | 說明 |
|---|---|
| INSENSITIVE / STATIC | 複製資料到 tempdb,隔離基礎表變更,唯讀 |
| SCROLL | 支援 FIRST/LAST/PRIOR/NEXT/RELATIVE/ABSOLUTE 導航 |
| FORWARD_ONLY | 僅支援 NEXT,效能最佳(預設) |
| READ_ONLY | 不可透過游標更新基礎表 |
OPEN cursor_name
FETCH [ NEXT | PRIOR | FIRST | LAST | ABSOLUTE n | RELATIVE n ]
FROM cursor_name
INTO @var1, @var2, ...
@@FETCH_STATUS:0=成功、-1=失敗/超出範圍、-2=列已刪除
CLOSE cursor_name; -- 釋放結果集,保留定義
DEALLOCATE cursor_name; -- 完全釋放資源
CREATE TABLE OriginSalary (O_ID NVARCHAR(20), O_Name NVARCHAR(50), O_Salary FLOAT);
CREATE TABLE AddSalary (O_ID NVARCHAR(20), A_Salary FLOAT);
INSERT INTO OriginSalary VALUES ('E001', '王小明', 50000), ('E002', '李大華', 45000), ('E003', '陳美麗', 55000);
INSERT INTO AddSalary VALUES ('E001', 5000), ('E002', 3000), ('E003', 4000);
CREATE PROCEDURE PK_SalaryAdd
AS
BEGIN
DECLARE @O_ID NVARCHAR(20), @A_Salary FLOAT;
DECLARE mycursor CURSOR FOR
SELECT O_ID, A_Salary FROM AddSalary;
OPEN mycursor;
FETCH NEXT FROM mycursor INTO @O_ID, @A_Salary;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE OriginSalary
SET O_Salary = O_Salary + @A_Salary
WHERE O_ID = @O_ID;
FETCH NEXT FROM mycursor INTO @O_ID, @A_Salary;
END
CLOSE mycursor;
DEALLOCATE mycursor;
END
GO
EXEC PK_SalaryAdd;
SELECT * FROM OriginSalary;
-- 結果:王小明 55000、李大華 48000、陳美麗 59000
-- 單一 UPDATE 完成,無需游標
UPDATE o
SET o.O_Salary = o.O_Salary + a.A_Salary
FROM OriginSalary o
JOIN AddSalary a ON o.O_ID = a.O_ID;
原則:能用集合操作解決,絕不使用游標。游標僅用於:
LOCAL FAST_FORWARD READ_ONLY 獲得最佳效能游標是 SQL Server 提供的強大工具,但「權力越大,責任越大」。理解其運作機制、效能代價、適用邊界,並在可能時優先選擇集合式操作,是資料庫開發者的必修課。