主鍵與聚集索引


在今天的文章里我想談下SQL Server里非常重要的話題,當人們第一次使用SQL Server時是最會混淆的。即主鍵約束(Primary Key constraint)和聚集索引(Clustered Index)的區別。

什么是主鍵(Primary Key)

首先讓我們談下主鍵約束本身。顧名思義它只是個約束,使用這個約束你告訴SQL Server你想在特定列或特定一組列有唯一值。下列代碼顯示了一個非常簡單的表定義,在第1個Col列指定了主鍵約束。

1 CREATE TABLE Foo
2 (
3     Col1 INT NOT NULL PRIMARY KEY,
4     Col2 INT NOT NULL,
5     Col3 INT NOT NULL
6 )
7 GO

現在當你往表里插入記錄,SQL Server確保在Col列總有唯一值。如果你嘗試插入重復值,SQL Server返回錯誤信息。

1 -- Try to insert a duplicate value
2 INSERT INTO Foo Values (1, 1, 1), (1, 2, 2)
3 GO

主鍵約束本身在邏輯層定義——你就告訴SQL Server你想在特定列有唯一值。但SQL Server也在物理層強制那個唯一性——你存儲你的表數據的數據結構里。在SQL Server情形里,唯一性使用索引結構在屋里層強制執行——使用聚集索引(Clustered Index)非聚集索引(Non-Clustered Index)。我們來詳細看下。

主鍵約束(Primary Key constraint)的強制執行

當你指定主鍵約束時,SQL Server在物理層默認通過使用唯一聚集索引(Unique Clustered Index)來強制執行。當你查看sys.indexes時,你會看到SQL Server內部已生成唯一聚集索引來強制執行主鍵約束。

1 -- SQL Server generates by default a Unique Clustered Index
2 SELECT * FROM sys.indexes
3 WHERE object_id = OBJECT_ID('Foo')
4 GO

我已經說過,默認是創建唯一聚集索引。你也可以使用如下代碼所示的唯一非聚集索引來強制執行主鍵約束。

1 -- Enforces the Primary Key constraint with a Unique Non-Clustered Index
2 CREATE TABLE Foo1
3 (
4     Col1 INT NOT NULL PRIMARY KEY NONCLUSTERED,
5     Col2 INT NOT NULL,
6     Col3 INT NOT NULL
7 )
8 GO

當你指定主鍵約束時,你可以指定下列2個選項:

  • CLUSTERED
  • NONCLUSTERED

CLUSTERED選項是默認的,因此你比需要指定它。當你再次查看sys.indexes時,現在你會看到在你面前有一個堆表(heap table)(沒有聚集索引定義的表),SQL Server已經生成另外的唯一非聚集索引來強制執行主鍵約束。

1 -- SQL Server has generated now a Unique Non-Clustered Index to
2 -- enforce the Primary Key constraint
3 SELECT * FROM sys.indexes
4 WHERE object_id = OBJECT_ID('Foo1')
5 GO

 

主鍵不等於聚集索引

因此在SQL Server里並不意味着主鍵和聚集索引總是一樣的。默認是一樣的,但你可要修改這個如果你想要的話。主鍵約束總是在邏輯層,索引結構在是物理層來強制約束本身。

現在的問題是什么時候使用唯一非聚集索引來強制主鍵約束是有意義的?在上個月我寫了一篇自增長的聚集鍵值不會擴展的文章,里面談到了所謂的最后頁插入閂鎖競爭(Last Page Insert Latch Contention)問題:在SQL Server里,像INT IDENTITY列這樣的自增長的聚集鍵列不會擴展(Scale)。

如果你想解決這個問題,或許在隨機值上物理聚集/排序你的表數據——像UNIQUEIDENTIFIER列。在這個情況下,你還可以使用在原始的自增長鍵列上使用主鍵約束,但它是使用唯一非聚集索引來強制的,在隨機鍵列上聚集你的表。下面代碼顯示了這個方法。 

 1 -- Create the Primary Key constraint on an ever-increasing
 2 -- key column
 3 CREATE TABLE Foo2
 4 (
 5     Col1 INT NOT NULL PRIMARY KEY NONCLUSTERED,
 6     Col2 UNIQUEIDENTIFIER NOT NULL,
 7     Col3 INT NOT NULL
 8 )
 9 GO
10 
11 -- Create the Clustered Index on a random key column
12 CREATE UNIQUE CLUSTERED INDEX ci_Col2 ON Foo2(Col2)
13 GO

當你再次查看sys.indexes時,現在你會看到你已經創建了聚集和非聚集索引。但只有非聚集索引用來強制主鍵約束。

1 -- Now we have a Clustered and Non-Clustered Index
2 SELECT * FROM sys.indexes
3 WHERE object_id = OBJECT_ID('Foo2')
4 GO

 

小結

在SQL Server里,主鍵約束和聚集索引並不一樣的。默認SQL Server使用唯一聚集索引來強制主鍵約束。但如果你想要的話,你可以使用唯一非聚集索引來代替。但這個方法默認是沒有太大意義,因為你需要處理相關問題(最后頁插入閂鎖競爭)來使用這個方法。

感謝關注!

參考文章:

https://www.sqlpassion.at/archive/2015/06/29/primary-key-vs-clustered-index/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM