在預存程序中正確管理交易,是確保資料一致性的基石。本文提供一個生產級的交易控制範本,涵蓋:多步驟 DML 錯誤累積、@@ERROR 捕捉、條件式提交/回滾、標準化回傳碼,可直接套用於進貨、銷貨、庫存調整等業務流程。
CREATE PROC usp_Stock
@GoodsId INT,
@Number INT,
@StockPrice MONEY,
@SupplierId INT,
@EmpId INT,
@StockUnit VARCHAR(50),
@StockDate DATETIME,
@TotalMoney MONEY,
@ActMoney MONEY,
@BaseId INT,
@Description NVARCHAR(255)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @error INT = 0; -- 錯誤計數器
BEGIN TRANSACTION; -- 開啟交易
/*------ 步驟 1:新增進貨主檔 ------*/
INSERT INTO StockInfo
(GoodsId, Number, StockPrice, SupplierId, EmpId,
StockUnit, StockDate, TotalMoney, ActMoney, Description, BaseId)
VALUES
(@GoodsId, @Number, @StockPrice, @SupplierId, @EmpId,
@StockUnit, @StockDate, @TotalMoney, @ActMoney, @Description, @BaseId);
SET @error += @@ERROR; -- 累加錯誤碼
/*------ 步驟 2:維護庫存明細 (UPSERT 邏輯) ------*/
IF EXISTS (SELECT 1 FROM dbo.InventoryInfo WHERE GoodId = @GoodsId)
BEGIN
-- 已有庫存記錄:累加數量與金額
UPDATE dbo.InventoryInfo
SET GNumber = GNumber + @Number,
TotalMoney = TotalMoney + @TotalMoney
WHERE GoodId = @GoodsId;
SET @error += @@ERROR;
END
ELSE
BEGIN
-- 首次進貨:插入新庫存記錄
DECLARE @GWarningNum INT;
SELECT @GWarningNum = WarningNum FROM dbo.GoodsInfo WHERE GId = @GoodsId;
INSERT INTO dbo.InventoryInfo
(GoodId, GNumber, BaseId, GWarningNum, TotalMoney, Remark, CreateDate)
VALUES
(@GoodsId, @Number, @BaseId, @GWarningNum, @TotalMoney, N'第一次進貨', GETDATE());
SET @error += @@ERROR;
END
/*------ 步驟 3:統一判斷提交或回滾 ------*/
IF @error <> 0
BEGIN
ROLLBACK TRANSACTION;
RETURN -1; -- 失敗代碼
END
ELSE
BEGIN
COMMIT TRANSACTION;
RETURN 1; -- 成功代碼
END
END;
GO
@errorSET @error += @@ERROR。@@ERROR 僅反映緊鄰前一條陳述式的錯誤狀態,必須即時捕捉,否則後續成功陳述式會將其重置為 0。@error <> 0 即可知道交易期間是否發生任何錯誤。BEGIN TRANSACTION 標示起點,COMMIT/ROLLBACK 標示終點。EXISTS,存在則 UPDATE 累加,不存在則 INSERT 新記錄。MERGE(舊版本相容性)或分開的 SELECT/INSERT 造成競態條件。RETURN 1:交易成功提交。RETURN -1:交易回滾,呼叫端可依此判斷後續處理(如記錄日誌、拋出例外)。若使用 SQL Server 2012+,建議改用 TRY...CATCH 結構配合 THROW,可捕捉嚴重度 11+ 的錯誤(@@ERROR 無法捕捉)。現代化寫法如下:
BEGIN TRY
BEGIN TRANSACTION;
-- 所有 DML 操作
COMMIT TRANSACTION;
RETURN 1;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
-- 記錄錯誤資訊:ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_LINE()
RETURN -1;
END CATCH;
UPDLOCK、HOLDLOCK 提示。採用結構化、可複用的交易控制模式,能顯著降低資料不一致風險,提升系統穩健度。