SQL Server 重置ID自增長
一、背景
SQL Server資料庫中表A中Id欄位的定義是:[Id] [int] IDENTITY(1,1),隨著資料的不斷增長,Id值已經接近2147483647(int的取值範圍為:-2 147 483 648 到 2 147 483 647)了,雖然已經對舊資料進行歸檔,但是這個表需要保留最近的1億資料,有什麼方法解決Id值就快爆的問題呢?
解決上面的問題有兩個辦法:一個是修改表結構,把Id的int資料型別修改為bigint;第二個是重置Id(Identity標識列)的值,使它重新增長。
當前標識值:current identity value,用於記錄和儲存最後一次系統分配的Id值;下次分配Id就是:當前標識值+標識增量(通常為+1,也可以自行設定);
當前列值:current column value,這Id值到目前為止的最大值;
二、重置過程
(一) 下面就測試重置Identity標識列,首先使用下面的SQL建立測試表:
--建立測試表 CREATE TABLE [dbo].[Test_Identity]( [IdentityId] [int] IDENTITY(1,1) NOT NULL, [Name] [nchar](10) NULL, CONSTRAINT [PK_testid] PRIMARY KEY CLUSTERED ( [IdentityId] ASC )WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
(二) 顯示插入Id值,插入後表[Test_Identity]的記錄如Figure1所示,接著再隱式插入Id值,插入後表[Test_Identity]的記錄如Figure2所示。
--顯示插入Id值 SET IDENTITY_INSERT [Test_Identity] ON INSERT INTO [Test_Identity](IdentityId,Name) SELECT 1000,'name1' SET IDENTITY_INSERT [Test_Identity] OFF --隱式插入Id值 INSERT INTO [Test_Identity](Name) SELECT 'name2'
(Figure1:資料記錄)
(Figure2:資料記錄)
(三) DBCC CHECKIDENT('table_name', NORESEED)不重置當前標識值。DBCC CHECKIDENT 返回一個報表,它指明當前標識值和應有的標識值。執行下面的SQL語句,返回的資訊表示:當前標識值'1001',當前列值'1001',如Figure2所示。
--查詢標識值 DBCC CHECKIDENT('Test_Identity', NORESEED) /* 檢查標識資訊: 當前標識值'1001',當前列值'1001'。 DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */
(四) 再隱式插入Id值,插入後表[Test_Identity]的記錄如Figure3所示。所以執行上面的SQL語句是不會重置當前標識值的,可以放心執行。
--隱式插入Id值 INSERT INTO [Test_Identity](Name) SELECT 'name3'
(Figure3:資料記錄)
--查詢標識值 DBCC CHECKIDENT('Test_Identity', NORESEED) /* 檢查標識資訊: 當前標識值'1002',當前列值'1002'。 DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */
(五) DBCC CHECKIDENT ('table_name') 或DBCC CHECKIDENT ('table_name', RESEED) 如果表的當前標識值小於列中儲存的最大標識值,則使用標識列中的最大值對其進行重置。
因為上面返回結果是:當前標識值'1002',當前列值'1002',所以執行下面的SQL語句是沒有影響的,什麼時候才有影響呢?參考:(當在Figure4狀態下執行下面的SQL命令,結果就會如Figure7所示)
--重置標識值 DBCC CHECKIDENT('Test_Identity', RESEED) /* 檢查標識資訊: 當前標識值'1002',當前列值'1002'。 DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */
(六) DBCC CHECKIDENT('table_name', RESEED, new_reseed_value)當前值設定為 new_reseed_value。如果自建立表後沒有將行插入該表,則在執行 DBCC CHECKIDENT 後插入的第一行將使用 new_reseed_value 作為標識。否則,下一個插入的行將使用 new_reseed_value + 1。如果 new_reseed_value 的值小於標識列中的最大值,以後引用該表時將產生 2627 號錯誤資訊。
要理解上面的描述,可以進行下面的測試:
1) 重新設定當前值設定為new_reseed_value = 995,執行下面的SQL語句返回的資訊如下所示;
--重置標識值 DBCC CHECKIDENT('Test_Identity', RESEED, 995) /* 檢查標識資訊: 當前標識值'1002',當前列值'995'。 DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */
2) 繼續往[Test_Identity]表插入資料,執行下面的SQL語句插入後的結果如Figure4所示;插入的Id值為new_reseed_value + 1 = 996;
--隱式插入Id值 INSERT INTO [Test_Identity](Name) SELECT 'name4'
(Figure4:資料記錄)
3) 檢視現在的標識值,與上面的進行對比,你就可以理解【當前標識值】與【當前列值】的意義了;
--查詢標識值 DBCC CHECKIDENT('Test_Identity', NORESEED) /* 檢查標識資訊: 當前標識值'996',當前列值'1002'。 DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */
4) 繼續往[Test_Identity]表插入資料,執行3次後表的資料如Figure5所示;
--隱式插入Id值 INSERT INTO [Test_Identity](Name) SELECT 'name5'
(Figure5:資料記錄)
5) 如果現在繼續往[Test_Identity]表插入資料會發生什麼事情呢?將產生 2627 號錯誤資訊,如下面的錯誤資訊;
訊息2627,級別14,狀態1,第2 行
違反了PRIMARY KEY 約束'PK_testid'。不能在物件'dbo.Test_Identity' 中插入重複鍵。
語句已終止。
6) 下面來測試建立表後沒有插入行,如果這個時候執行重置標識值會發生什麼事情?清空[Test_Identity]表,再重新設定標識值,返回的資訊如下面所示;
--清空表 truncate table [Test_Identity] --重置標識值 DBCC CHECKIDENT('Test_Identity', RESEED, 995) /* 檢查標識資訊: 當前標識值'NULL',當前列值'995'。 DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */
7) 這個時候往[Test_Identity]表插入資料,資料就如Figure6所示,這說明了:“如果自建立表後沒有將行插入該表,則在執行 DBCC CHECKIDENT 後插入的第一行將使用 new_reseed_value 作為標識。”
--隱式插入Id值 INSERT INTO [Test_Identity](Name) SELECT 'name5'
(Figure6:資料記錄)
(Figure7:資料記錄)
8) 假如我們刪除了IdentityId為1000和1001的記錄,這個時候繼續插入資料,會重新生成1000和10001值嗎?效果如Figure10所示(重新覆蓋了);
--刪除和 delete from [Test_Identity] where IdentityId=1000 delete from [Test_Identity] where IdentityId=1001
(Figure8:資料記錄)
--重置標識值 DBCC CHECKIDENT('Test_Identity', RESEED, 996) --隱式插入Id值 INSERT INTO [Test_Identity](Name) SELECT 'name6'
(Figure9:資料記錄)
(Figure10:資料記錄)
(七) 總結:到這裡,我們已經可以解決Id值就快爆的問題了,因為我們舊的資料會定時歸檔,所以不會出現2627錯誤資訊;而另外一個場景是當出現Figure5的時候,可以執行DBCC CHECKIDENT('Test_Identity', RESEED),設定為當前列最大值為標識值,防止出現2627錯誤資訊。
三、補充說明
在MySQL中,也有類似Identity的功能:
`IDs` int(11) unsigned NOT NULL AUTO_INCREMENT
在建立表的時候,會有一個選項AUTO_INCREMENT=17422061,直接可以設定起始值,還可以設定步長:
SHOW VARIABLES LIKE 'auto_inc%';
起始值:auto_increment_offset
步長:auto_increment_increment
SET @auto_increment_increment=10;
SELECT LAST_INSERT_ID();
四、參考文獻