事務中的變量(包括表變量)的操作是不受事務控制的。但是反過來,事務中的變量操作失敗,卻會導致事務提交失敗,這個有點讓人鬱悶。
下面的腳本演示這個問題。示例演示分拆以逗號分隔的 @ids 中的每個 id, 如果這個 id 是數字(int型),則做後面的處理;如果不是數字(賦值失敗,進入CATCH塊),則跳過這個id,處理下一個。整個處理在一個事務中進行。
DECLARE
@ids varchar(1000);
SET @ids = '1,a,3,';
BEGIN TRY
BEGIN TRAN;
-- 循環分拆@ids 中以逗號分隔的每個id
WHILE CHARINDEX(',', @ids) > 0
BEGIN
DECLARE
@id int;
BEGIN TRY
-- 獲取當前的id
SET @id =LEFT(@ids, CHARINDEX(',', @ids) - 1);
END TRY
BEGIN CATCH
-- 從@ids 中去掉當前的id
SET @ids = STUFF(@ids, 1, CHARINDEX(',', @ids), '');
CONTINUE;
END CATCH
-- 從@ids 中去掉當前的id
SET @ids = STUFF(@ids, 1, CHARINDEX(',', @ids), '');
-- 處理id
RAISERROR('current id: %d', 10, 1, @id) WITH NOWAIT;
END
COMMIT TRAN;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRAN;
SELECT
err_line = ERROR_LINE(),
err_message = ERROR_MESSAGE();
END CATCH
這個腳本執行後,輸出消息如下:
current id: 1
current id: 3
err_line err_message
----------- -------------------------------------------------------
30 當前事務無法提交,而且無法支持寫入日誌文件的操作。請回滾該事務。
(1 行受影響)
這個結果表明,@ids 中的所有 id 確實在循環中處理過,但第二個id由於不是數字,所以跳過了。但最終提交事務失敗了,因爲第二個id不是數字,導致賦值失敗,從而導致事務不可提交。
發現這個問題是因爲公司在用Service Broker傳遞業務數據,由於傳遞的xml數據有問題,導致類型不是期望有類型,從而使處理失敗。
個人覺得這個問題有點令人討厭,按照我的理解,既然不受事務控制,那麼它也不應該反過來影響事務,可是結果與理解的不一樣,值得注意。
最後補充一下:寫這個的重點在於提醒大家,在事務處理中,不要忽略那些看起來與事務無關的處理,它們出錯一樣會影響事務的最終提交。當然,解決的辦法是把這與事務無關的操作放在事務外(不過如果是存儲過程嵌套,就不好控制)。另外一個,只要處理可能會在事務中,則出錯時都拋出錯誤,而不試圖忽略錯誤,這樣可以解決問題。