在 SQL 中,对于大规模更改,通常需要实现“要么全部成功,要么全部失败”的场景——要么所有更改都通过,要么在任何错误情况下都回滚。这种方法通过使用事务来实现。存储过程应该明确开始一个事务(BEGIN TRANSACTION),将相应操作包装起来,处理错误,并在故障发生时执行 ROLLBACK,否则执行 COMMIT。
需要记得通过类似 TRY...CATCH(SQL Server)或 EXCEPTION(PostgreSQL)的结构来正确处理错误。
SQL Server 示例:
CREATE PROCEDURE MassUpdate AS BEGIN BEGIN TRY BEGIN TRANSACTION; UPDATE Orders SET Status = 'Processed' WHERE Status = 'Pending'; UPDATE Inventory SET Stock = Stock - 1 WHERE ProductID IN (SELECT ProductID FROM Orders WHERE Status = 'Processed'); COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; -- 错误日志记录 THROW; END CATCH END
问题: 仅使用 BEGIN TRANSACTION 和 COMMIT 是否足够保证所有支持的数据库管理系统在出错时正确回滚更改?
回答: 不,事务本身并不会捕获异常。需要使用处理器(TRY...CATCH 或类似方法)来捕获错误,并明确调用 ROLLBACK。在某些数据库(例如,使用自动提交的 MySQL)中,可能还需要额外的配置。
错误代码示例(SQL Server):
BEGIN TRANSACTION; UPDATE Users SET Name = 'Ivan' WHERE ID = 1; UPDATE Orders SET Amount = Amount/0 WHERE UserID = 1; -- 除以零错误 COMMIT TRANSACTION;
在这种情况下,如果发生错误——事务就会保持打开状态,而第一个表中的更改可能会被保存。
历史
在一个电子商务项目中,开发人员忘记在事务中处理错误,导致关联数据部分丢失:如果在更新订单时发生库存更新问题,仅部分更改被回滚,导致信息完整性受到影响。
历史
在 BI 分析项目中,实施了通过过程进行大规模报告的重新计算,但没有明确控制事务和捕获错误。结果:部分报告被更新,另一部分则没有。最终数据变得不一致,因为紧急情况未能导致原子性回滚。
历史
在一家公司错误地依赖于 MySQL 的自动提交,而没有为大型操作设置事务模式。在服务器故障时,部分数据已被记录,另一部分则没有,这导致了漫长的恢复工作和部分订单的丢失。