2014-01-15 42 views
0

我創建了一個名爲tblEmployees與代碼表 -SQL Server標識列的行爲不正確?

Create table tblEmployees 
(
    EmployeeID int identity primary key, 
    Name nvarchar(30), 
    Salary float, 
    Gender tinyint 
) 

然後我插入值 -

insert into tblEmployees values ('Akmal', 5000, 0) 
insert into tblEmployees values ('Shakira', 6000, 1) 
insert into tblEmployees values ('Kiron', 7000, 2) 
insert into tblEmployees values ('Jamil', 5500, 0) 
insert into tblEmployees values ('Faul', 4800, 4) 

但是,當顯示的值 -

EmployeeID Name  Salary Gender 
2   Akmal  5000   0 
3   Shakira 6000   1 
4   Kiron  7000   2 
5   Jamil  5500   0 
7   Faul  4800   4 

我的問題是,爲什麼EmployeeID列以2開頭? 6在哪裏?不應該自動增加?

+2

'IDENTITY(1,1)'本應以1爲增量開始標識列。 – user2989408

+0

問題在於,如果EmployeeID存在差距,它就很重要。 – Zane

回答

2

您的T-SQL腳本不完整,因爲我的IDENTITY值是從1(以5結尾)開始生成的。

注#0:我只是試圖描述一些缺少IDENTITY值的原因。

注意#1:不要在生產服務器上運行此腳本。

注2:列定義中使用IDENTITY裝置IDENTITY(1,1) < =>IDENTITY(seed value/initial value=1,increment value=1)

注意#3:如果您不知道consequences of this command,您應該避免使用DBCC CHECKIDENT

的第一遺漏值(一個可能的解釋):

爲什麼僱員柱開始2?

運行以下腳本:

IF OBJECT_ID(N'dbo.tblEmployees') IS NOT NULL 
    DROP TABLE dbo.tblEmployees; 
GO 
Create table tblEmployees 
(
    EmployeeID int identity primary key, 
    Name nvarchar(30), 
    Salary float, 
    Gender tinyint 
) 
GO 
insert into tblEmployees values ('Akmal', 5000, 0) 
insert into tblEmployees values ('Shakira', 6000, 1) 
insert into tblEmployees values ('Kiron', 7000, 2) 
insert into tblEmployees values ('Jamil', 5500, 0) 
insert into tblEmployees values ('Faul', 4800, 4) 
GO 
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #1]; 
/* 
Last IDENTITY #1 
---------------- 
5 
*/ 
GO 

此時此表中生成的最後IDENTITY值(因爲你可以看到)5,而不是7(如你的例子)。

SELECT * FROM dbo.tblEmployees; 
/* 
EmployeeID Name       Salary     G 
----------- ------------------------------ ---------------------- - 
1   Akmal       5000     0 
2   Shakira      6000     1 
3   Kiron       7000     2 
4   Jamil       5500     0 
5   Faul       4800     4 
*/ 
GO 

所有行都有連續的IDENTITY值:沒有間隙。

現在,由於種種原因,有人將刪除所有dbo.tblEmployees行還決定重置(RESEED)的最後一個標識值(5)到1(從5到1)。

DELETE dbo.tblEmployees; 
GO 
DBCC CHECKIDENT('dbo.tblEmployees', RESEED, 1); 
GO 
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #2]; 
/* 
Last IDENTITY #2 
---------------- 
1 
*/ 
GO 

現在,最後的IDENTITY值是1(因爲RESEED 1)。

insert into tblEmployees values ('Akmal', 5000, 0) 
insert into tblEmployees values ('Shakira', 6000, 1) 
insert into tblEmployees values ('Kiron', 7000, 2) 
insert into tblEmployees values ('Jamil', 5500, 0) 
insert into tblEmployees values ('Faul', 4800, 4) 
GO 
SELECT * FROM dbo.tblEmployees; 
GO 
/* 
EmployeeID Name       Salary     Gender 
----------- ------------------------------ ---------------------- ------ 
2   Akmal       5000     0 
3   Shakira      6000     1 
4   Kiron       7000     2 
5   Jamil       5500     0 
6   Faul       4800     4 
*/ 

當我再次插入這些行時,第一次生成的IDENTITY值是2(這次)。

爲什麼?究其原因,MSDN描述:

「如果沒有行已插入到表中,因爲表的創建,或如果全部行已經通過 TRUNCATE TABLE語句刪除,第一行後,您插入運行DBCC CHECKIDENT使用new_reseed_value作爲身份 否則,插入的下一行使用new_reseed_value +當前增量值

這最後一個公式解釋了爲什麼這一次的第一身份值是2:

new_reseed_value(是1 - 因爲RESEED 1)+當前增量值(1-參見注釋#2)= 1 + 1 = 2

注#4:如果使用的TRUNCATE TABLE代替DELETE然後TRUNCATE TABLE之後插入第一行將具有ID =種子值(見注2)或new_reseed_value = 1。因此,在這種情況下,不這樣做需要DBCC(..., RESEED, 1)

第二缺失值(一個可能的解釋):

在哪裏是6?

DELETE dbo.tblEmployees WHERE EmployeeID = 6 
insert into tblEmployees values ('Faul', 4800, 4) 
GO 
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #3]; 
/* 
Last IDENTITY #3 
---------------- 
7 
*/ 
GO 
SELECT * FROM dbo.tblEmployees; 
GO 
/* 
EmployeeID Name       Salary     Gender 
----------- ------------------------------ ---------------------- ------ 
2   Akmal       5000     0 
3   Shakira      6000     1 
4   Kiron       7000     2 
5   Jamil       5500     0 
7   Faul       4800     4 
*/ 
15

不要依靠IDENTITY列來產生一組連續的值,且沒有間隙。期。這是不能保證的;有幾件事情會導致差距,如回滾,刪除,重播等。我不相信你用上面的確切代碼重現了這個問題;這些INSERT陳述之間可能還有其他活動。

對於這種代理和無意義的價值,你真的不應該在意是否存在差距。如果您關心差距,請使用不同的技術(例如,可序列化的max()+ 1解決方案) - 請注意,爲了可伸縮性/併發性考慮,您需要交換差距。對方的回答(你接受了,但我懷疑會被刪除)說:

如果你想有那麼可靠和指定的值標識列需要IDENTITY_INSERT設置爲ON該列,插入值(具有特定的ID值),然後將IDENTITY_INSERT設置爲OFF。

這隻有在您已經知道要插入該列的值時纔有效。這首先破壞了IDENTITY財產的目的。如果您還不知道要插入什麼值(例如「下一個」ID是什麼),這意味着您需要從表中獲取SELECT MAX(),並向其中添加1。這意味着整個事情需要可序列化,否則其他人可以讀取相同的值並將相同的+1添加到它。因此,除了使IDENTITY屬性無用,如果您始終要覆蓋生成的值,它還會通過有效地將併發性限制爲1來殺死可伸縮性。我強烈建議您在實施該方法之前大力權衡該方法。

我建議你做的是,使用IDENTITY列,不要掛在差距。他們將會發生,對此你沒有太多的辦法,反正它也不應該成爲一個問題。誰在乎是否沒有員工#6?

+0

你能解釋爲什麼嗎? – user2989408

+0

現在沒有那麼有趣了,因爲我有一個數據庫在共享的託管SQL Server上跳過了1000個ID。至少我知道別人看到了它的古怪。 –

+3

@邁克爾,有點不同;請參閱[連接上的此錯誤](http://connect.microsoft.com/SQLServer/feedback/details/739013/failover-or-restart-results-in-reseed-of-identity)。它與在SQL Server 2012中引入SEQUENCE時身份值的緩存機制如何改變有關。 –

-4

不知道爲什麼我以前的答案被否決並刪除或阻止,但如果你想有可靠和指定的值標識列,那麼你需要IDENTITY_INSERT設置爲ON該列,插入您的值(具有特定的ID值),然後將IDENTITY_INSERT設置回OFF。

如果你不在乎最終的值是什麼(這可能是僱員表的情況),那麼就忘記它,並學習如何處理這些值。

+1

請看我的答案,爲什麼這個建議擺弄'IDENTITY_INSERT'完全沒有意義。 –

+0

現實生活中的情況:我必須從以前的實現中導入查找表的數據。爲了向後兼容,我必須尊重先前實現中的ID,並且在此之後,所有新的查找值都將獲得自動生成的值。 在這種情況下,解決方案是編寫IDENTITY_INSERT設置爲OFF然後打開的部署後腳本。 對不起,與你不同,我不是在說理論,而是在說真實的生活情況。 – NicVerAZ

+0

你寫道:「另外,要做到這一點,你需要知道要插入什麼值,這意味着你需要從表中選擇MAX(),哪個整體需要可序列化,否則其他人可以讀取你的MAX ()值並添加相同的+1「Dude,閱讀我寫的:插入具有特定ID值的值。 您是否在編碼之前閱讀要求? – NicVerAZ