发现这个代码错误导致了生产中的死循环:
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猜测是否存在此类错误的有效方法,因为我使用游标的经验从未见过连续两次检查故意(至少没有嵌套游标,但那些仍然有控制语句,例如OPEN
和CLOSE
检查@@FETCH_STATUS
外部游标之间)。
PS[condition]
这一次相当于“不是DST过渡日”(这是这个星期天)。更多时区错误!
我对语言特性最感兴趣,以实现与其他语言如何处理 for 循环的事后考虑子句(例如i++
in for (int i = 0; i < myArray.Length; i++)
)相同的自动提供的保证。个人负责的事情越少,出错的地方就越少,审计我们系统中成千上万的存储过程将是一项艰巨的任务,特别是因为其中许多不恰当地迭代数据而不是使用已经基于集合的逻辑。
为了避免这样的问题,我使用不同的模式进行获取:
在这种模式中,您
FETCH
只有一次,您也需要检查一次获取状态。