Sql-Server

在缺少 FETCH NEXT 的情況下防止無限循環

  • February 19, 2022

發現這個程式碼錯誤導致了生產中的死循環:

DECLARE @BatchID INT
DECLARE MyCursor CURSOR FOR
   SELECT BatchID = ...
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO @BatchID
WHILE @@FETCH_STATUS = 0
BEGIN
   ...
   IF [condition]
   BEGIN
       ...
       FETCH NEXT FROM MyCursor INTO @BatchID
   END
END
CLOSE MyCursor
DEALLOCATE MyCursor

假設需要一個游標是有保證的,有沒有辦法防止這個錯誤(除了更多的測試/程式碼審查)?

在其他語言中,我們FOREACH有為我們管理迭代進度的循環,或者FOR事後考慮的循環,那麼 SQL Server 中是否有任何等價物可以防止諸如放錯位置(或忘記!)之類的草率錯誤?

我從未見過 SQL 中的自定義循環需要對游標做任何花哨WHILE的事情,並且循環具有相同的風險(以及@BatchID 為DELETE #WorkData WHERE ID = @BatchID時的情況),那麼對於典型案例,如何以程式方式/乾淨地減輕這種風險?NULL


像這樣的方法非常不吸引人:

DECLARE @BatchID INT
DECLARE MyCursor CURSOR FOR
   SELECT BatchID = -1--dummy entry to always be skipped
   UNION ALL SELECT BatchID = ...
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO @BatchID
WHILE @@FETCH_STATUS = 0
BEGIN
   FETCH NEXT FROM MyCursor INTO @BatchID
   IF @@FETCH_STATUS = 0
   BEGIN
       ...
   END
END
CLOSE MyCursor
DEALLOCATE MyCursor

在我看來,@@FETCH_STATUS在中間沒有任何游標位置/控制語句的情況下連續檢查兩次可能是 SQL Server猜測是否存在此類錯誤的有效方法,因為我使用游標的經驗從未見過連續兩次檢查故意(至少沒有嵌套游標,但那些仍然有控制語句,例如OPENCLOSE檢查@@FETCH_STATUS外部游標之間)。

PS[condition]這一次相當於“不是DST過渡日”(這是這個星期天)。更多時區錯誤!

我對語言功能最感興趣,以實現與其他語言如何處理 for 循環的事後考慮子句(例如i++in for (int i = 0; i < myArray.Length; i++))相同的自動提供的保證。個人負責的事情越少,出錯的地方就越少,審計我們系統中成千上萬的儲存過程將是一項艱鉅的任務,特別是因為其中許多不恰當地迭代數據而不是使用已經基於集合的邏輯。

為了避免這樣的問題,我使用不同的模式進行獲取:

OPEN Cursor;

WHILE 1=1
BEGIN 
   
   FETCH NEXT FROM Cursor INTO [...];
   IF @@FETCH_STATUS <> 0 BREAK;

   [...]

END;

CLOSE Cursor;
DEALLOCATE Cursor;

在這種模式中,您FETCH只有一次,您也需要檢查一次獲取狀態。

引用自:https://dba.stackexchange.com/questions/252602