Sql-Server

將主 id 欄位更改為唯一標識符 GUID 作為 sql server 中的預設值

  • May 14, 2017

我有帶有標識規範 (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(我假設是集群的),即:

  1. 緊湊:每行 4 個字節
  2. 高效:比較是簡單的 4 字節值到 4 字節值(即二進制比較)
  3. 最小碎片化:新值是連續的並添加到表的末尾。

您希望將其更改為集群 PK,即:

  1. 寬:每行72 個字節(GUID 為 16 個字節,即 32 個字元串形式的十六進制值字元加上 4 個破折號共 36 個字元,在 UTF-16 / 中為 72 個字節NVARCHAR)。
  2. 低效:如果使用UNIQUEIDENTIFIER它並沒有那麼糟糕,因為它仍然是二進制比較,就像 with 一樣INT,但它是 16 個字節INT,而不是 4。將其儲存為字元串現在是 36 個字元的比較(36 個字節VARCHAR和 72 個字節NVARCHAR)這比 16 字節慢UNIQUEIDENTIFIER。最後,大多數將 GUID 儲存為字元串的人忘記使用二進制排序規則(例如Latin1_General_100_BIN2)來至少逐字節進行比較,因為不需要語言規則。使用不區分大小寫的排序規則,甚至是區分大小寫的排序規則,肯定會更慢,因為它將應用基於語言環境的語言規則。
  3. 高度碎片化:新值到處都是,導致頁面拆分大大增加。而且,如果您減少FILLFACTOR以減少頁面拆分的數量,那麼您也會降低索引的性能,因為它分佈在大量頁面上。

請記住,由於以下原因,此更改的下游負面影響:

  1. 聚集索引鍵被複製到非聚集索引中。再次假設這個 PK 是聚集的,那麼這個表上的每個非聚集索引都會將 72 字節的值複製到其中。此表上的三個非聚集索引是 72 字節 * 3 = 216 字節加上原來的 72 聚集索引 = 288 字節總和。另一方面,目前INT只有 4 個字節 * 3 = 12 個字節加上原來的 4 = 16 個字節。每行。
  2. 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 。

我找到了一篇關於如何刪除和重新創建所有外鍵的好文章,也許你可以在這個問題上使用它。

引用自:https://dba.stackexchange.com/questions/173503