簡體   English   中英

在SQL Server數據庫的非IDENTITY類型字段中重新分配ID

[英]Re-assigning IDs in a non-IDENTITY type field in SQL Server database

警告:這個悲慘的故事包含代碼氣味和糟糕的設計決策以及技術債務的例子。

如果您熟悉SOLID原則,請練習TDD並對您的工作進行單元測試, 請勿閱讀 除非你想要對某人的不幸進行一次好的傻笑,並且在你自己很棒的事情中幸災樂禍地知道你永遠不會為你的繼任者留下如此巨大的廢話。

所以,如果你坐得舒服,我會開始。

在這個應用程序中,我已經繼承並且在過去的7個月中一直在支持和修復錯誤,我在6個半月之前離開了一個開發人員的DOOZY。 是的,我開始后2周。

無論如何。 在這個應用程序中,我們有clientsemployeesvisits表。

還有一個名為AppNewRef (或類似的東西)的表......等待它......包含用於其他每個表的下一個記錄ID。 因此,可能包含以下數據: -

TypeID    Description     NextRef
   1      Employees       804
   2      Clients         1708
   3      Visits          56783

當應用程序為Employees創建新行時,它會在AppNewRef表中查找,獲取值,使用該值作為ID,然后更新NextRef列。 對於ClientsVisits以及其中使用的NextID存儲在此處的所有其他表也是如此。

是的,我知道,此數據庫上沒有自動編號的IDENTITY列。 所有這些都是“當它是一個Access應用程序”的借口。 這些ID保存在(VB6)代碼中。 因此,可能有多達20億1.47億條記錄。 好的,這似乎運作得相當好。 (除了應用程序正在更新和處理鎖定/更新等事實,而不是數據庫)

因此,我們的用戶非常樂意創建EmployeesClientsVisits等。 Visits ID一直穩定增加幾十個。 然后問題就發生了。 我們的客戶在創建批量訪問時會導致數據庫損壞,因為服務器很好地丟失了,應用程序變得沒有響應。 因此,他們使用任務管理器殺死應用程序,而不是耐心等待。 當然應用確實似乎鎖定了。

轉到今年早些時候,開發人員蒂姆(真實姓名。沒有保護有罪的人)開始修改代碼以分階段進行批量更新,以便UI保持“響應”。 然后四月來了,他正在做他的通知(你現在可以想象現場,不是嗎?)他正在努力完成更新。

4月底,5月初我們更新了一些客戶。 在接下來的幾個月里,我們會越來越多地更新它們。

蒂姆(真名,記得)和我(在蒂姆離開前兩周開始)以及一周后開始的另一位新開發者看不見,訪問表中的ID開始向上跳躍。 巨大的,我的意思是10000,20000,30000一次。 有時幾十萬。

這是一個圖表,說明了使用的ID的快速增長。

看看他的圖表

11月滾。 客戶致電技術支持並報告他收到了錯誤消息。 我查看錯誤消息並詢問數據庫,以便我可以調試代碼。 我發現這個值太大了很長時間。 我做了一些查詢,提取信息,將其放入Excel並繪制圖形。

我不認為使代碼處理的時間長於ID,這是正確的方法,因為這個應用程序將該ID傳遞給其他DLL和OCX,並打破那些看起來像是一個整個世界的傷害我不喜歡現在不想見。

我正在研究的一個潛在想法是嘗試修改ID,以便我可以將它們降低到較低的水平。 基本上填補了空白。 使用ROW_NUMBER函數

我正在考慮做的是為每個表添加一個新列,這些表具有對這些訪問ID的外鍵引用(不是正確的外鍵思維,這個數據庫中不存在這些約束)。 這個新列可以存儲訪問ID的舊(當前)值(哦,只是為了混淆事物;在某些表上它被稱為EventID ,在某些表上它被稱為VisitID )。

然后,對於引用該VisitID每個其他表,更新為新值。

想法? 建議? T-SQL的片段幫助所有人感激不盡。

方案一:

顯式約束所有外鍵關系,並將它們設置為ON UPDATE CASCADE

這意味着無論何時更改ID,外鍵都會自動更新。

然后你就像這樣運行......

WITH
  resequenced AS
(
  SELECT
    ROW_NUMBER() OVER (ORDER BY id) AS newID,
    *
  FROM
    yourTable
)
UPDATE
  resequenced
SET
  id = newID

我沒有在年齡這樣做,讓我忘了,如果它通過具有相同的id值兩個記錄會導致問題,年年更新。 如果確實如此,你可以先做這樣的事情......

UPDATE yourTable SET id = -id


方案二:

確保未明確定義任何外鍵關系。 如果是,請注意他們丟棄並刪除它們。

然后做一些像......

CREATE TABLE temp AS
  newID INT IDENTITY (1,1),
  oldID INT
)

INSERT INTO temp (oldID) SELECT id FROM yourTable

/* Do this once for the table you are re-identifiering              */
/* Repeat this for all fact tables holding that ID as a foreign key */
UPDATE
  factTable
SET
  foreignID = temp.newID
FROM
  temp
WHERE
  foreignID = temp.oldID

然后重新應用任何現有的外鍵關系。

這是一個非常可怕的選擇。 如果您忘記更新表格,則只需填寫數據即可。 但是,你可以給那個temp表一個更好的名字並保持它。


祝好運。 願主可以憐憫你的靈魂。 如果你在黑暗的小巷里見過他,那就是蒂姆。

我會創建一個數字表,其中只有一個從1到最大的序列,增量為1是長的,然后更改獲取visitid的maxid的邏輯,也許其他人在數字和訪問表之間進行正確的連接。 然后你可以找到那個數字的最小值

select min(number) from visits right join numbers on visits.id = numbers.number

這樣你就可以在不改變任何其他表格的情況下填補所有空白。

但我只是重做整個數據庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM