DECLARE @BookId INT, @BName VARCHAR(50), @BIndex INT;
DECLARE Book_Cursor CURSOR FOR
SELECT BookId, BookName, BookIndex FROM Bas_BookList;
OPEN Book_Cursor;
FETCH NEXT FROM Book_Cursor INTO @BookId, @BName, @BIndex;
WHILE @@FETCH_STATUS = 0
BEGIN
-- 處理當前列:UPDATE/DELETE/INSERT 等
FETCH NEXT FROM Book_Cursor INTO @BookId, @BName, @BIndex;
END
CLOSE Book_Cursor;
DEALLOCATE Book_Cursor;
@@FETCH_STATUS:0=成功、-1=失敗、-2=找不到列 (游標結尾)。
BEGIN TRANSACTION Tran1;
BEGIN TRY
-- 業務邏輯
COMMIT TRANSACTION Tran1;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION Tran1;
-- 錯誤記錄/拋送
END CATCH
@@TRANCOUNT:巢狀交易計數器,BEGIN TRAN 遞增,COMMIT 遞減至 0 才真正提交,ROLLBACK 直接歸零回滾至最外層起點。
using (var conn = new SqlConnection(connStr)) {
conn.Open();
using (var tran = conn.BeginTransaction()) {
try {
using (var cmd = new SqlCommand(sql, conn, tran)) {
cmd.ExecuteNonQuery();
}
tran.Commit();
} catch {
tran.Rollback();
throw;
}
}
}
BEGIN TRY
-- 可能出錯的 TSQL 陳述式
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrNo,
ERROR_MESSAGE() AS ErrMsg,
ERROR_SEVERITY() AS Severity,
ERROR_STATE() AS State,
ERROR_LINE() AS ErrLine,
ERROR_PROCEDURE() AS ProcName;
-- 可在此記錄錯誤日誌、發送警示
END CATCH
捕捉運行時錯誤 (嚴重度 11-19),編譯錯誤 (語法錯誤) 不會進入 CATCH。搭配 THROW (SQL 2012+) 重新拋送錯誤保留堆疊。
場景:需逐筆處理大量暫存表資料,每筆邏輯複雜,要求全部成功或全部失敗,且需精確定位失敗列。
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION BatchTran;
DECLARE @ArrearageId BIGINT, @ComId BIGINT, @AgentId BIGINT;
DECLARE A_Cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT Id, ProviceName, CityName FROM Ass_ArrearageTemporary;
OPEN A_Cursor;
FETCH NEXT FROM A_Cursor INTO @ArrearageId, @ComId, @AgentId;
WHILE @@FETCH_STATUS = 0
BEGIN
-- 複雜業務邏輯:驗證、計算、寫入多張表
-- 例如:INSERT INTO FormalTable ...; UPDATE Summary SET ...;
FETCH NEXT FROM A_Cursor INTO @ArrearageId, @ComId, @AgentId;
END
COMMIT TRANSACTION BatchTran;
CLOSE A_Cursor;
DEALLOCATE A_Cursor;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
-- 確保游標資源釋放再回滾
IF CURSOR_STATUS('global', 'A_Cursor') >= 0
BEGIN
CLOSE A_Cursor;
DEALLOCATE A_Cursor;
END
ROLLBACK TRANSACTION BatchTran;
END
-- 記錄錯誤資訊
INSERT INTO ErrorLog (ErrTime, ErrProc, ErrMsg, ErrLine)
VALUES (GETDATE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), ERROR_LINE());
RETURN; -- 結束預存程序
END CATCH
LOCAL FAST_FORWARD (唯讀、僅前進) 效能最佳,避免預設動態游標開銷。INSERT...SELECT、MERGE、UPDATE...JOIN 解決的,堅決不用游標。游標僅處理「必須逐列、邏輯無法集合化」的極少數場景。游標、交易、錯誤處理是 SQL Server 程式設計的鐵三角。掌握游標類型選擇、交易巢狀行為、結構化異常捕捉,並以「集合操作優先、游標兜底、交易包裹、錯誤可觀測」為原則,才能寫出健壯、高效、可維護的資料庫程式碼。