對於游標內的每次迭代,是否可以獨立地發生回滾/送出?
我們有這個會計解決方案生態系統,基本上,它有兩張表:
dbo.AccountFluxRecord ( Id, TotalCredits, TotalDebits, CreditMinusDebit, Application (the application used to insert the record) ) dbo.AccountFluxRecordDetail ( Id, AccountFluxId, (FK to above table) Type, (Credit or Debit) Value, CreditBankAccountId, (nullable FK) DebtBankAccountId (nullable FK) )
我們有一些應用程序使用它,並且都使用儲存過程來插入新記錄。
SP_InsertAccountFluxRecord
我們將列表作業系統詳細資訊作為自定義使用者表類型傳遞給 SP。它進行了大量驗證,以確保所有記錄至少有一個貸方和一個借方,並且貸方減去債務的總和為零。它還驗證 BankAccountId 是否對每種操作都有效。
幾年來,它在數百名客戶中完美執行,每週插入數百萬條記錄。我們為大多數客戶託管數據庫,但其中一些堅持他們的數據必須在內部託管。我們可以接受,因為我們可以獲得一個開放的維護渠道。
今天,我們遇到了其中一位客戶自託管數據庫的問題。
我們發現了十幾個“壞”記錄。這些記錄缺少債務詳細資訊,這是不可能的,因為該特定應用程序從一對借記/貸記詳細資訊創建了一個 AccountFluxRecord,其中一個是另一個的鏡像。如果沒有一對有效的銀行賬戶,它甚至無法接受使用者輸入,但我們發現在這些不良記錄中 DebtBankAccountId 欄位為空。
我們保留所有操作的日誌,日誌反映了錯誤記錄按原樣插入,沒有刪除或更新。Obs:我們確實使用快照事務模式來避免我們過去遇到的一些死鎖問題。
TLDR 我得到了十幾個無效記錄,由 SP 插入,以阻止插入無效記錄,就好像繞過了所有應用程序和儲存過程驗證一樣。下面有一個片段顯示瞭如何在 SP 中處理驗證。
IF (@SumDetailDebit <> @SumDetailCredit) BEGIN set @Mesage = @ErrorPrefix + N'TOTAL DEBIT IS DIFFERENT FROM CREDIT!'; THROW 50000, @Mesage ,1 END
**問題:**在進行一些驗證後,在 SP 內有一個游標為每次迭代插入詳細記錄。即使我們沒有在循環內使用事務控制,是否可以送出一個插入而回滾另一個插入?
經過大量測試,我找到了答案。是的,它可以,因為您執行它是 AD HOC,沒有事務控制。
create table dbo.TOBEINSERTEDINTO ( ID int not null ) GO /*********************************************************************/ /*** Test scenario ***/ /*********************************************************************/ create procedure TestCursor as begin declare @id int declare cursor_test cursor read_only fast_forward for select * from (values(1),(2),(3),(null)) as src(id) order by id desc open cursor_test declare @status int = 0 while @status = 0 begin fetch next from cursor_test into @id set @status = @@fetch_status if (@status = 0) begin if (@id is null) begin throw 50000, n'!!!!!!!!! id cannot be NULL !!!!!!!!!!' ,1 end insert into dbo.TOBEINSERTEDINTO(ID) values(@id) end end close cursor_test deallocate cursor_test end; GO /*********************************************************************/ /*** Calling inside a transaction, with a try..catch can be ok ***/ /*********************************************************************/ begin tran begin try Exec TestCursor end try begin catch if @@trancount > 0 rollback end catch if @@trancount > 0 commit transaction; select * from dbo.TOBEINSERTEDINTO GO /*********************************************************************/ /*** Lack of transaction control leads to inconsistence ***/ /*********************************************************************/ exec TestCursor GO select * from dbo.TOBEINSERTEDINTO GO
該應用程序獲得了可靠的事務控制。
問題的產生是因為有人篡改了基地。我猜有人執行了一個配置文件並試圖模仿一些應用程序呼叫。