1. 程式人生 > >SQL Server 重置ID自增長

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'
複製程式碼

F1

(Figure1:資料記錄)

F2

(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'

F3

(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'

F4

(Figure4:資料記錄)

3) 檢視現在的標識值,與上面的進行對比,你就可以理解【當前標識值】與【當前列值】的意義了;

複製程式碼
--查詢標識值
DBCC CHECKIDENT('Test_Identity', NORESEED)
/*
檢查標識資訊: 當前標識值'996',當前列值'1002'。
DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。
*/
複製程式碼

4) 繼續往[Test_Identity]表插入資料,執行3次後表的資料如Figure5所示;

--隱式插入Id值
INSERT INTO [Test_Identity](Name)
SELECT 'name5'

F5

(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'

F6

(Figure6:資料記錄)

F7

(Figure7:資料記錄)

8) 假如我們刪除了IdentityId為1000和1001的記錄,這個時候繼續插入資料,會重新生成1000和10001值嗎?效果如Figure10所示(重新覆蓋了);

--刪除和
delete from [Test_Identity] where IdentityId=1000
delete from [Test_Identity] where IdentityId=1001

F8

(Figure8:資料記錄)

--重置標識值
DBCC CHECKIDENT('Test_Identity', RESEED, 996)
--隱式插入Id值
INSERT INTO [Test_Identity](Name)
SELECT 'name6'

F9

(Figure9:資料記錄)

F10

(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();

四、參考文獻