1. 程式人生 > 其它 >SQLServer:內部事務與外部事務的統一

SQLServer:內部事務與外部事務的統一

在我們日常資料庫開發過程中,涉及到資料增刪改,無一例外都需要使用事務來控制其一致性。

而對於業務邏輯比較複雜的ERP/MES/WMS等系統來說,一致性尤其重要。

此時我們考慮一個問題,儲存過程A開啟了事務,此時B由於業務需要呼叫儲存過程A,但是B又有自己的業務,也需要開啟事務。此時我們就需要考慮事務的一致。

而受SQLServer的事務機制影響,在第一次commit之後,事務計數就會重新,等到我們commit第二個事務的時候就會報錯:EXECUTE後的事務計數指示BEGINCOMMIT語句的數目不匹配。上一計數 = 1,當前計數 = 0。

除了巢狀這一種比較麻煩的方式之外,還有另外一種更簡單的方式,下面我們模擬一下

ProcA:

begin try

begintrantran_ProcA

  --ProcA的業務

  exec ProcB

commit tran tran_ProcB

end try

begin catch

  rallback tran tran_ProcA

end catch

ProcB:

declare @tranCount int

set @tranCount = @@TRANCOUNT

begin try

if @tranCount = 0

  begin tran tran_ProcB;

  --ProcA的業務

  if@tranCount = 0

    commit tran tran_ProcB;

end try

begin catch

  if @tranCount = 0

    rollback tran tran_ProcB;

end catch

為了區分A和B,所以單獨對ProcB增加了對事務的判斷,原理很簡單,就是我再執行ProcB的時候,判斷事務有沒有開啟,當單獨呼叫ProcB的時候,@@TRANCOUNT = 0,所以就會開啟事務。

但是如果在ProcA中呼叫ProcB的時候,事務已經開啟的,所以ProcB就不會再開啟事務了,如果有另外的儲存過程再呼叫ProcA,那麼也需要在ProcA中增加對@@TRANCOUNT的判斷,主要要先賦值,且再進入事務之前就建立變更接收@@TRANCOUNT的值,因為後面@@TRANCOUNT會隨著事務的進行發生變化。

當然,這種方法也是有弊端的,因為對於複雜的業務來說,我們在對資料提交前會做很多的校驗,例如我裝箱之前要判斷是否有庫存,是否已經被裝箱,箱號是否重複等等資訊,再加上揀貨/反揀等等的狀態控制。其實我們會對很多表去關聯查詢來檢驗這個資料是否合法,顯然這些都不適合放在事務內去操作,放在事務外我們使用with(nolock)關聯查詢,可以極大避免事務提交慢而鎖住一堆業務表,最終導致業務癱瘓。所以我們也要遵循一個原則,事務內只處理增刪改,無關的表最好連查詢都不要放進去。

現在寫了不少MES業務的儲存過程之後再回想這些,其實我們都沒必要弄這麼複雜。儲存過程裡調儲存過程本身就不是一個好的選擇,還是要推薦根據具體業務分析出合理的優化方式