在今天的文章里我想談下SQL Server里非常重要的話題,當人們第一次使用SQL Server時是最會混淆的。即主鍵約束(PRimary Key constraint)和聚集索引(Clustered Index)的區別。
什么是主鍵(Primary Key)首先讓我們談下主鍵約束本身。顧名思義它只是個約束,使用這個約束你告訴SQL Server你想在特定列或特定一組列有唯一值。下列代碼顯示了一個非常簡單的表定義,在第1個Col列指定了主鍵約束。
1 CREATE TABLE Foo2 (3 Col1 INT NOT NULL PRIMARY KEY,4 Col2 INT NOT NULL,5 Col3 INT NOT NULL6 )7 GO
現在當你往表里插入記錄,SQL Server確保在Col列總有唯一值。如果你嘗試插入重復值,SQL Server返回錯誤信息。
1 -- Try to insert a duplicate value2 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 Index2 SELECT * FROM sys.indexes3 WHERE object_id = OBJECT_ID('Foo')4 GO
我已經說過,默認是創建唯一聚集索引。你也可以使用如下代碼所示的唯一非聚集索引來強制執行主鍵約束。
1 -- Enforces the Primary Key constraint with a Unique Non-Clustered Index2 CREATE TABLE Foo13 (4 Col1 INT NOT NULL PRIMARY KEY NONCLUSTERED,5 Col2 INT NOT NULL,6 Col3 INT NOT NULL7 )8 GO
當你指定主鍵約束時,你可以指定下列2個選項:
CLUSTERED選項是默認的,因此你比需要指定它。當你再次查看sys.indexes時,現在你會看到在你面前有一個堆表(heap table)(沒有聚集索引定義的表),SQL Server已經生成另外的唯一非聚集索引來強制執行主鍵約束。
1 -- SQL Server has generated now a Unique Non-Clustered Index to2 -- enforce the Primary Key constraint3 SELECT * FROM sys.indexes4 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 GO10 11 -- Create the Clustered Index on a random key column12 CREATE UNIQUE CLUSTERED INDEX ci_Col2 ON Foo2(Col2)13 GO
當你再次查看sys.indexes時,現在你會看到你已經創建了聚集和非聚集索引。但只有非聚集索引用來強制主鍵約束。
1 -- Now we have a Clustered and Non-Clustered Index2 SELECT * FROM sys.indexes3 WHERE object_id = OBJECT_ID('Foo2')4 GO
在SQL Server里,主鍵約束和聚集索引并不一樣的。默認SQL Server使用唯一聚集索引來強制主鍵約束。但如果你想要的話,你可以使用唯一非聚集索引來代替。但這個方法默認是沒有太大意義,因為你需要處理相關問題(最后頁插入閂鎖競爭)來使用這個方法。
感謝關注!
新聞熱點
疑難解答