2009-09-26 50 views
0

通常,當您指定標識列時,您將在SQL Server中獲得一個方便的界面以詢問特定的行。有誰知道重複使用身份值的巧妙方法嗎?

SELECT * FROM $IDENTITY = @pID 

如果標識列因爲只能有一個,您並不需要關心名稱。

但是如果我有一張主要由臨時數據組成的表格。大量的插入和大量的刪除。有沒有簡單的方法讓我重用身份值。

最好我希望能夠寫一個函數,將返回說作爲下一個身份值NEXT_SMALLEST($IDENTITY),並以故障安全的方式這樣做。

基本上找到未使用的最小值。這並不是完全無關緊要,但我想要的是能夠告訴SQL Server這是我的函數,它將生成標識值。但我知道的是,沒有這樣的功能存在...

我想...

實現全局數據庫的ID,我需要提供我的控制是一個默認值。

我的想法是基於我應該能夠擁有一個具有所有已知ID的表,然後來自某個需要全局ID的其他表的每個行ID都會引用該表。默認值將由類似提供

INSERT INTO GlobalID 
RETURN SCOPE_IDENTITY() 

回答

4

不要重複使用身份,你只需將自己拍攝下來就可以了。使用足夠大的值,以便它永遠不會翻轉(64位大整數)。

要查找的數字序列缺失差距加入表對本身就帶有+/- 1的區別:

SELECT a.id 
FROM table AS a 
LEFT OUTER JOIN table AS b ON a.id = b.id+1 
WHERE b.id IS NULL; 

這個查詢將在其ID-1不是ID序列發現號在表中,即。連續的序列起始數字。然後您可以使用SET IDENTITY INSERT OFF插入一個特定的ID和reuse的數字。與普通的基於身份的插入相比,這樣做的代價是壓倒性的(運行時和代碼複雜度)。

5

否;它可以重複使用並不是唯一的。

爲什麼你想重新使用它們?你爲什麼關心這個領域?如果你想要控制它,不要把它變成一個身份;創建你自己的方案並使用它。

+0

身份列有很好的屬性,但我想提供我自己的函數來生成身份值,我非常知道那是不能完成的,但我仍然想知道是否有體面的工作,我沒有看到。 – 2009-09-26 15:29:14

1

如果你真的要重置IDENTITY值最低,
這裏是您可以通過DBCC CHECKIDENT使用

基本上下面的SQL語句重置標識值,以便從最低可能的數字標識值重新啓動

create table TT (id int identity(1, 1)) 
GO 
insert TT default values 
GO 10 
select * from TT 
GO 
delete TT where id between 5 and 10 
GO 
--; At this point, next ID will be 11, not 5 
select * from TT 
GO 
insert TT default values 
GO 
--; as you can see here, next ID is indeed 11 
select * from TT 
GO 
--; Now delete ID = 11 
--; so that we can reseed next highest ID to 5 
delete TT where id = 11 
GO 

--; Now, let''s reseed identity value to the lowest possible identity number 
declare @seedID int 
select @seedID = max(id) from TT 
print @seedID --; 4 

--; We reseed identity column with "DBCC CheckIdent" and pass a new seed value 
--; But we can't pass a seed number as argument, so let's use dynamic sql. 
declare @sql nvarchar(200) 
set @sql = 'dbcc checkident(TT, reseed, ' + cast(@seedID as varchar) + ')' 
exec sp_sqlexec @sql 
GO 

--; Now the next 
insert TT default values 
GO 
--; as you can see here, next ID is indeed 5 
select * from TT 
GO 
+0

是的,但這很瘋狂。它甚至會工作嗎?身份值範圍內的漏洞會不會最終趕上並拋出錯誤? – 2009-09-26 15:31:42

+0

@約翰:是的,它的作品。我知道這是不實際的,但DBCC CHECKIDENT可供您使用。你的問題沒有提及身份號碼之間的任何漏洞,如果你不想要任何*漏洞*,那麼這不是你所需要的。請記住,每個解決方案*都取決於您所處的上下文。 – Sung 2009-09-27 18:11:17

1

我想我們真的需要知道您爲什麼要重複使用您的標識列。我能想到的唯一原因是由於數據的臨時性,您可能會耗盡身份的可能值。這不太可能,但如果這是您的擔憂,那麼可以使用uniqueidentifiers(guid)作爲表中的主鍵。

函數newid()將創建一個新的guid,並可用於插入語句(或其他語句)。然後當你刪除該行時,你的密鑰中沒有任何「漏洞」,因爲guid不是按照這個順序創建的。

1

[語法假定SQL2008 ....]

是的,這是可能的。您需要兩個管理表和每個參與表上的兩個觸發器。

首先,管理表:

-- this table should only ever have one row 
CREATE TABLE NextId (Id INT) 
INSERT NextId VALUES (1) 
GO 

CREATE TABLE RecoveredIds (Id INT NOT NULL PRIMARY KEY) 
GO 

然後,觸發器,兩個在每個表:

CREATE TRIGGER tr_TableName_RecoverId ON TableName 
FOR DELETE AS BEGIN 
    IF @@ROWCOUNT = 0 RETURN 
    INSERT RecoveredIds (Id) SELECT Id FROM deleted 
END 
GO 

CREATE TRIGGER tr_TableName_AssignId ON TableName 
INSTEAD OF INSERT AS BEGIN 
    DECLARE @rowcount INT = @@ROWCOUNT 
    IF @rowcount = 0 RETURN 
    DECLARE @required INT = @rowcount 
    DECLARE @new_ids TABLE (Id INT PRIMARY KEY) 
    DELETE TOP (@required) OUTPUT DELETED.Id INTO @new_ids (Id) FROM RecoveredIds 
    SET @rowcount = @@ROWCOUNT 
    IF @rowcount < @required BEGIN 
    DECLARE @output TABLE (Id INT) 
    UPDATE NextId SET Id = Id + (@[email protected]) 
    OUTPUT DELETED.Id INTO @output 
    -- this assumes you have a numbers table around somewhere 
    INSERT @new_ids (Id) 
    SELECT n.Number+o.Id-1 FROM Numbers n, @output o 
    WHERE n.Number BETWEEN 1 AND @[email protected] 
    END 
    SET IDENTITY_INSERT TableName ON 
    ;WITH inserted_CTE AS (SELECT _no = ROW_NUMBER() OVER (ORDER BY Id), * FROM inserted) 
     , new_ids_CTE AS (SELECT _no = ROW_NUMBER() OVER (ORDER BY Id), * FROM @new_ids) 
    INSERT TableName (Id, Attr1, Attr2) 
    SELECT n.Id, i.Attr1, i.Attr2 
    FROM inserted_CTE i JOIN new_ids_CTE n ON i._no = n._no 
    SET IDENTITY_INSERT TableName OFF 
END 

你可以從腳本系統表中的觸發器出很輕鬆了。

你會想測試這個併發性。它應該按原樣工作,儘管如此,語法錯誤:OUTPUT子句保證了id查找的原子性 - >增量爲一步,並且整個操作發生在事務中,這要歸功於觸發器。

TableName.Id仍然是標識列。像$ IDENTITY和SCOPE_IDENTITY()這樣的常見習慣用法仍然有效。

桌子上沒有中心的ID表,但可以輕鬆創建一個。

+0

我真正想要做的就是將此標量值UDF作爲使用全局ID的表的缺省值,它將負責創建新的ID並且數據庫需要其餘的照顧。 – 2010-02-22 09:23:58

+0

不能用w標量UDF來完成。 UDF不允許副作用,並且沒有副作用,所以無法增加全局ID。 – 2010-02-22 12:37:33

-1

我沒有尋找未使用的值的任何幫助,但如果你真的想找到他們,將它們自己,你可以使用

set identity_insert on .... 

在你的代碼這樣做。

雖然我和其他人一樣。何必?你沒有業務問題要解決嗎?

相關問題