最近,我被委派调试一款电子商务应用程序中的一个奇怪问题。在应用程序升级后,该网站开始不时地挂起,因此我被派去进行调试。在检查事件日志后,我发现SQL服务器在几分钟内写入了大约 200,000 条事件,并显示失败了一个约束条件的消息。经过长时间的调试和一些追踪,我找到了罪魁祸首。我已删除了一些不必要的代码并做了一些清理,但基本上就是这样。
WHILE EXISTS (SELECT * FROM ShoppingCartItem WHERE ShoppingCartItem.PurchID = @PurchID)
BEGIN
SELECT TOP 1
@TmpGFSID = ShoppingCartItem.GFSID,
@TmpQuantity = ShoppingCartItem.Quantity,
@TmpShoppingCartItemID = ShoppingCartItem.ShoppingCartItemID,
FROM
ShoppingCartItem INNER JOIN GoodsForSale on ShoppingCartItem.GFSID = GoodsForSale.GFSID
WHERE ShoppingCartItem.PurchID = @PurchID
EXEC @ErrorCode = spGoodsForSale_ReverseReservations @TmpGFSID, @TmpQuantity
IF @ErrorCode <> 0
BEGIN
Goto Cleanup
END
DELETE FROM ShoppingCartItem WHERE ShoppingCartItem.ShoppingCartItemID = @TmpShoppingCartItemID
-- @@ROWCOUNT is 1 after this
END
事实:
- 只有一个或两个记录与第一个选择子句匹配
- 从DELETE语句的RowCount指示已被删除
- WHILE子句将永远循环
该存储过程已被重写,将应该被删除的行选择到一个临时内存表中,因此立即问题得到了解决,但这确实激发了我的好奇心。
为什么它会一直循环?
澄清:删除没有失败(在调试后,delete stmt后的 @@rowcount为1) 澄清2:SELECT TOP ... 子句是否按任何特定字段排序都无关紧要,因为返回的id对应的记录将被删除,所以在下一次循环中应该获取另一个记录。
更新:经过检查,我找到了导致这个存储过程失控的罪犯提交。我唯一发现的真正区别是,在SELECT TOP 1语句中先前没有加入连接,也就是说,没有那个连接它可以在不使用任何事务语句的情况下运行。似乎引入连接使SQL服务器变得更加挑剔。
更新澄清:brien 指出没有必要加入连接,但我们实际上确实使用了GoodsForSale表中的一些字段,但为了使代码简单,我已将它们删除,以便我们可以专注于手头的问题。