單元測試與資料表自動遞增Id的問題

今天同事問了一個關於資料庫建立資料表重設Id遞增值的問題,主要是在單元測試中,使用的資料庫並不是每次都重新建立,而是在執行單元測試之前,將資料表中的資料刪除,並且將Id欄位的初始值重設。
為了達到上面的需求,所以在一開始的時候會執行刪除資料和DBCC CHECKIDENT (‘TableName’, RESEED, 0)將資料表的遞增種子重設為0,這樣在新增資料的時候就會+1變成從1開始(一般正常的情況)。

不過問題是,如果資料庫和資料表是第一次剛建立的,使用這個指令會讓Id欄位一開始的編號是0,期望的初始編號是1,但是在之後刪除資料並且使用上面的指令後,再新增資料的初始編號就會從1開始,符合期望的數字。

這個問題我試了一下,發現如果是資料庫與資料表剛建立的時候,若是執行DBCC CHECKIDENT (‘TableName’, NORESEED)會取得NULL,但是新增資料的時候Id會是1(沒有執行ReSeed的情況下),這部份感覺上應該是資料庫有自行處理這一個部份,所以若是在一開始執行ReSeed,反而是讓資料庫不處理NULL到1的部份,新增資料時的Id就變成是0。

原本建議同事在執行完單元測試的時候刪除資料時再做ReSeed的動作,不過他說有時候想要在新增資料後看看資料庫的內容,所以不想要立即刪除資料,因此詢問如何在執行單元測試之前判斷資料庫或資料表是否剛建立?

針對這個問題,我回到如何得知資料庫內的種子遞增值的方向來思考,如何在執行ReSeed之前取得目前的值,判斷是否為NULL再來針對接下來執行相對應的動作。

簡單查了一下,可以利用IDENT_CURRENT( ‘table_or_view’ )這個語法來得知目前資料表的遞增值,透過Select IDENT_CURRENT( ‘table_or_view’ ) As Seed來取得,後面的As Seed部份是將取得的值放在名為Seed名稱的欄位,沒有這一段也沒關係,只是沒有欄位名稱。

從微軟的文件庫中還有看到IDENT_SEED ( ‘table_or_view’ )這個指令,是用來查詢最初設定欄位的遞增值,因為有時候可能會設定自動遞增的欄位不是從1開始,這時候就可以利用這個函數來查詢初始的設定,回傳的結果並不會受到DBCC CHECKIDENT重設的影響,也許以後會用到。

2020/05/27 Update:

後來同事告知使用Select IDENT_CURRENT( ‘table_or_view’ ) As Seed的方式仍然會有問題,也就是說當資料表剛被建立,尚未新增資料的時候,IDENT_CURRENT函數取得的是1,新增多筆資料刪除之後再ReSeed設0,IDENT_CURRENT函數取得也會是1。

因為當資料表剛被建立尚未新增資料前,若是使用DBCC CHECKIDENT來ReSeed設0,第一筆資料新增的時候會直接使用ReSeed時給定的初始值(也就是0)。但是若資料表有新增過資料再刪除,重新ReSeed為0的時候,作法變成是給定的初始值+1,從上面的例子ReSeed為0的情況再+1就變成1,所以兩個情況都會取得1。

至於上次為何我會取到NULL值,應該是我當時在測試的時候沒有留意,執行IDENT_CURRENT函數的時候是在資料表尚未建立的時候(不存在的TableName),所以取得的結果是NULL。

印象中上次在查詢資料的時候有看到另外的解法,就是去撈取系統資料表中的記錄,只是語法麻煩了一點,這次再把它找出來試了一下,並且留意資料表建立前後的問題,結果是可行的,語法如下:

IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = ‘TableName‘ AND last_value IS NOT NULL)
DBCC CheckIdent(‘TableName‘, ReSeed, 0);
Else
Print(‘no need reseed’)

參考來源:

發佈留言

%d 位部落客按了讚: