[SQL][問題處理]T-SQL 內的錯誤處理

[SQL][問題處理]T-SQL 內的錯誤處理

今天有個朋友提到一個蠻有趣的問題,關於 「SQL Server 交易完成之後,是否會遺失資料的狀況 ?」。在我處理和 SQL Server 打交道的經驗中,實在是沒有遇過這樣的狀況,但在朋友的環境中卻是明明有執行命令且收到執行成功,但都會偶發有資料不見,實在是想不出來是什麼原所造成的,在關聯式的交易管理中,應該是絕對符合 ACID 的特性才對啊。因此花了一點時間看了一下他的程式和處理,看來是有些蠻特別的地方,因此我大致把狀況模擬一下,當成一個範例來說明一下。

 

在測試環境內我們先建立一個範例資料表

CREATE TABLE A1( A INT PRIMARY KEY , B CHAR(5) )

 

很單純的建立一個資料表,並且測試放了資料進去,確認是沒有問題的。在下述的指令當中,我們固定 INSERT 相同的資料兩次,最後透過 SELECT 指令可以看到相同鍵值的紀錄確實只有一筆資料會存入。

INSERT INTO A1 VALUES ( 1 , 'A')
GO 2

SELECT * FROM A1
GO

 

在朋友的環境內,會有類似以下的處理

BEGIN 

	INSERT INTO A1 VALUES ( 1 , 'A');

	IF ( @@ERROR > 0 )
		PRINT 'ERROR'
	ELSE
		PRINT 'OK'
END

因此執行之後看起來很正常,會如同我們所預期的,顯示我們期望當有異常的時候顯示「ERROR」的訊息

image

 

但因為一些需求變更和改版,可能原本的 SQL 指令會變成如下

BEGIN 
	SELECT 1/0
	INSERT INTO A1 VALUES ( 2 , 'A');

	IF ( @@ERROR > 0 )
		PRINT 'ERROR'
	ELSE
		PRINT 'OK'
END

此時雖然第一個 SELECT 1/0 會發生錯誤,但最後還是會顯示出「OK」的訊息

image

 

或者是原本的程式為了要查問題,又做了一些調整,變成如下的樣子

BEGIN 
	
	INSERT INTO A1 VALUES ( 2 , 'A');
	PRINT 'DEBUG'
	IF ( @@ERROR > 0 )
		PRINT 'ERROR'
	ELSE
		PRINT 'OK'
END

image

 

為什麼會有這樣的狀況呢 ? 主要是因為用 @@ERROR 的變數值,主要是抓最近一次執行指令的狀況,因此很有可能因為後來在原本的指令內又多加了一些處理,導致判斷上會不如我們所期望的。而要解決這樣的問題,在 SQL Server 2005 之後,則可以搭配 TRY CATCH 的處理機制,就可以不用寫上一堆 IF 的判斷式,可以將原本的指令寫成如下的樣式來處理:

BEGIN TRY
	INSERT INTO A1 VALUES ( 5 , 'X');
	SELECT 1/0
END TRY
BEGIN CATCH
	IF ( ERROR_NUMBER() > 0 )
		PRINT 'ERROR'
	ELSE
		PRINT 'OK'
END CATCH

GO 2

而我們可以從訊息中看到,執行兩次都會根據不同的狀況,結果都會如我們所期望的去顯示「ERROR」

image

 

上述的範例有些簡略,只是想說明使用 @@ERROR 和 TRY CATCH 的差異,很多時候問題都只是發生在這些細節的過程中,如果開發的過程中可以更仔細了解相關的差異,勢必可以減少不少除錯的時間,否則要找這些錯誤,有些時候還真是大海撈針,只能去碰碰運氣了,此次剛好運氣比較湊巧,可以發現到有異常的原因,否則要慢慢的去查 SQL Trace & 交易紀錄檔,那可就真的累人了。