將主 id 欄位更改為唯一標識符 GUID 作為 sql server 中的預設值
我有帶有標識規範 (1,1) 的 id(primary key - int) 表。
我想將此列更改為
uniqueidentifier
預設 (newid()
) 。我試過的是
ALTER TABLE myTable ALTER COLUMN id uniqueidentifier default NEWID();
但我收到了這條資訊
Incorrect syntax near the keyword 'default'.
此外,我的數據庫的每個表都有
id
主鍵列,我想通過循環或其他方式將它們更改為uniqueidentifier
預設值。newID()
我的數據庫的每個表都有 id 主鍵列,我想用預設的 newID() 將它們更改為 nvarchar(36)
不,您不想做出這種改變,因為它不會帶來任何好處。
您目前有一個 PK(我假設是集群的),即:
- 緊湊:每行 4 個字節
- 高效:比較是簡單的 4 字節值到 4 字節值(即二進制比較)
- 最小碎片化:新值是連續的並添加到表的末尾。
您希望將其更改為集群 PK,即:
- 寬:每行72 個字節(GUID 為 16 個字節,即 32 個字元串形式的十六進制值字元加上 4 個破折號共 36 個字元,在 UTF-16 / 中為 72 個字節
NVARCHAR
)。- 低效:如果使用
UNIQUEIDENTIFIER
它並沒有那麼糟糕,因為它仍然是二進制比較,就像 with 一樣INT
,但它是 16 個字節INT
,而不是 4。將其儲存為字元串現在是 36 個字元的比較(36 個字節VARCHAR
和 72 個字節NVARCHAR
)這比 16 字節慢UNIQUEIDENTIFIER
。最後,大多數將 GUID 儲存為字元串的人忘記使用二進制排序規則(例如Latin1_General_100_BIN2
)來至少逐字節進行比較,因為不需要語言規則。使用不區分大小寫的排序規則,甚至是區分大小寫的排序規則,肯定會更慢,因為它將應用基於語言環境的語言規則。- 高度碎片化:新值到處都是,導致頁面拆分大大增加。而且,如果您減少
FILLFACTOR
以減少頁面拆分的數量,那麼您也會降低索引的性能,因為它分佈在大量頁面上。請記住,由於以下原因,此更改的下游負面影響:
- 聚集索引鍵被複製到非聚集索引中。再次假設這個 PK 是聚集的,那麼這個表上的每個非聚集索引都會將 72 字節的值複製到其中。此表上的三個非聚集索引是 72 字節 * 3 = 216 字節加上原來的 72 聚集索引 = 288 字節總和。另一方面,目前
INT
只有 4 個字節 * 3 = 12 個字節加上原來的 4 = 16 個字節。每行。- PK 經常被 FK 使用,也就是將 PK 複製到一個或多個其他表中。每行 72 個字節僅用於此表。如果此 PK 在其他 2 個表中用作外鍵,則為 72 個字節 * 2 = 144 個額外字節。另一方面,電流
INT
只有 4 字節 * 2 = 8 字節。FK 列是否被索引?如果是,那麼那是另外 72 個字節而不是 4 個字節。 3. 數據頁在它們的行可以被讀取和使用之前被載入到記憶體(即緩衝池)中。更大的行和/或更低的行
FILLFACTOR
意味著需要更多的數據頁來保存這些行。這意味著有更多時間將它們從磁碟讀取到記憶體中,顯然它們需要更多的記憶體。這需要與其他查詢、計劃記憶體等競爭。如果您需要一個 GUID 來獲得外部系統已知的值,只需添加一
UNIQUEIDENTIFIER
列並為其編制索引;然後,您可以查找它以獲取INT
用於所有其他 JOIN 等的值。並且,如果必須將其儲存為字元串,則使用VARCHAR
給定的唯一字元是A
,B
,C
,D
,E
,F
和-
(NVARCHAR
是完全沒有必要的浪費空間)並確保指定二進制排序規則(以 結尾_BIN2
)。但是不要改變目前的結構。
關於問題的更新,其中澄清了要更改的預期數據類型是真的
UNIQUEIDENTIFIER
而不是NVARCHAR(36)
:總體建議不會從“不要這樣做”改變。雖然UNIQUEIDENTIFIER
是比NVARCHAR(36)
(更小並且比較是二元的)更好的選擇,但它實際上只是一個“不那麼糟糕”的選擇,而不是“更好/好的選擇”。
有一個大問題你沒有考慮過:
- 據我所知,您不能從列中刪除 IDENTITY。
- 因此,您只能將此列轉換為整數、大整數、小整數、小整數或小數或小數位數為 0 的數字
不是好消息,但您可以嘗試另一種方法:
- 添加新列
- 用新值填充它
- 刪除 PRIMARY KEY 約束。
- 刪除 ID 列
- 將新列重命名為 Id
- 在 Id 列上添加 PRIMARY KEY 約束
CREATE TABLE MyTable ( Id int IDENTITY(1,1), F1 int, CONSTRAINT [PK_MyTable] PRIMARY KEY (Id) ); INSERT INTO MyTable VALUES (10),(20),(30); --= Add a new column ALTER TABLE MyTable ADD Id2 uniqueidentifier DEFAULT NewID() NOT NULL; --= Fill new field with values UPDATE MyTable SET Id2 = NewID(); --= Drop PRIMARY KEY ALTER TABLE MyTable DROP CONSTRAINT [PK_MyTable]; --= Drop Id column ALTER TABLE MyTable DROP COLUMN Id; --= Rename Id2 column as Id EXEC sp_RENAME 'MyTable.Id2', 'Id', 'COLUMN'; --= Add PRIMARY KEY ALTER TABLE MyTable ADD CONSTRAINT [PK_MyTable] PRIMARY KEY (Id); SELECT * FROM myTable; GO
F1 | ID -: | :----------------------------------- 30 | e430b87c-6fbf-43c3-a88c-00abe925c2ea 20 | 67e6d0ac-3bb8-4c29-b19b-501ebdc2acee 10 | 287117fb-0188-480f-be54-ef42cacc123a 警告:更改對象名稱的任何部分都可能破壞腳本和儲存過程。
dbfiddle在這裡
即使這可行,讓我嘗試添加一個新表:
CREATE TABLE MyTable2 ( Id int, MyTable_Id int NOT NULL, CONSTRAINT [PK_MyTable2] PRIMARY KEY (Id), CONSTRAINT [FK_MyTable2] FOREIGN KEY (Id) REFERENCES MyTable(Id) );
是的,有一個引用 MyTable 的 Id 欄位的 FOREIGN KEY。現在嘗試再次執行腳本並…
消息 3725 級別 16 狀態 0 第 25 行 約束“PK_MyTable”被表“MyTable2”引用,外鍵約束“FK_MyTable2”。 消息 3727 級別 16 狀態 0 第 25 行 無法刪除約束。請參閱以前的錯誤。
dbfiddle在這裡
是的,又是一個壞消息,要刪除 PRIMARY KEY,您應該刪除之前引用此欄位的所有 FOREIGN KEYS 。
我找到了一篇關於如何刪除和重新創建所有外鍵的好文章,也許你可以在這個問題上使用它。