學會了基本的SQL指令後,下一個主題就是關於效能。
索引
索引的目標,是快速定位資料列。以下我採用Baraa對索引三個面向的介紹,注意這只是用三個面向去看索引,分類彼此並非互斥的。
- Storage Format: 依照資料physically的儲存方式,可以分成Rowstore Index和Columnstore Index兩種
- Functions & Features: 這比較像索引的屬性,Unique, Filtered的特性可以套到不同的索引上
- Access Structure: 這是指B-tree結構上的差異,Rowstore Index和Columnstore Index之下都各自有Clustered Index和Non-clustered Index兩種

資料庫如何儲存資料?
在談索引之前,要先理解資料庫如何儲存資料。
底層的資料結構
SQL Server 儲存資料最小的單位是頁面(data page),每個頁面大小固定為 8 KiB【官方文件】。Page有不同種類,但主要需要理解的就是 Index page 和 Data page。
頁面結構
下圖是一個 Data Page,每個 Data Page 開頭有頁面標頭(圖中的Header),紀錄頁碼、頁面類型等資訊,頁面末端有 slot array 儲存每一列在頁面中的偏移量(圖中的Offset)。圖中的1:150的意思是file Id:page number。

而 Index Page 不存放資料,而是存放一個指向 Data Page 的 Pointer。

沒有索引的堆積表(Heap)
沒有索引被建立的狀態就是 Heap 官方文件,這裡指的索引是 Clustered Index,在後面會詳細介紹。Heap 可以被視為一個 Data Page 集合,而且 Data Page 內部的 rows 都沒有排序過。

堆積表使用 Row Identifier(RID)來定位資料列。若沒有任何索引,資料庫必須掃描整個表 (Full Table Scan) 才能找到符合條件的資料,只有在表很小時才勉強可接受。當資料量成長後,實務系統都需要索引來避免這種全表掃描。
Clustered Index:唯一的實體排序方式
當你對一個表的某幾個 columns 建立 Clustered Index,資料庫會做兩件事情:
- Physically sort all the data rows
- Build a B+ tree

Clustered Index 會把資料列依索引鍵值排序並存放在 B+ 樹的葉節點中。這是 SQL Server 唯一會實際改變資料列物理排序的索引類型,因此一個表只能有一個叢集索引。
注意 Data Page 在 leaf node,而且內部的資料列都已經排序過後,也就是因為有排序過,才可以透過索引透過範圍快速找到資料的位置。
Non-clustered Index:獨立的查詢捷徑
不同於 Clustered Index,Non-clustered Index「不會」改變資料存放方式,但提供額外快速搜尋的捷徑。因為它與資料分離,一個表可以建立多個非叢集索引,以支援不同的查詢模式。
以下這張圖顯示的是 Non-Clustered Index 的 Index Page,會直接定位到 Data Page 中特定的資料列,因為他是透過 RID (row identifier),所以又被稱為 Row Locator Page。

因為Non-Clustered Index不會實體排序資料,整體建立完索引的概念圖如下:

Index Page 又可以被其他 Index Page 指向,最後建立成一顆B+ tree。

Non-clustered Index 的這棵 B+ tree 和 Clustered Index 的 B+ tree 差在他的 Leaf Nodes 是不存資料的。
剛剛說到 Non-clustered Index 可以建立多個,所以當然也可以和 Clustered Index 並存,概念上就會如下,同一組 Data Page 有許多不同的 Index 種類去加入查詢到資料列的位置。

總結一下兩種不同的 B+ tree 結構有哪些特性差異:
- Read performance: clustered比較好,因為少一層
- Write performance: non-clustered比較好,因為不用重新排序
- Storage Efficiency: clustered比較好,因為少一層
- Use cases: Clustered Index 適合用在很少會更值的 data column,而且可以增進 range query performance,因為資料已經排序過
複合索引與索引鍵順序
最後我們談談複合索引,這指的是一個索引是包含多個欄位還是單一欄位?複合索引(composite index)是指索引鍵包含多個欄位。
在使用複合索引時,建立的順序和查詢的順序要符合Leftmost Prefix Rule,否則索引不會被有效利用。
例如建立 (A, B, C) 的索引時,查詢若使用 A 或 A+B,可以有效利用索引;若跳過 A 直接使用 B,則索引的排序順序與查詢條件不一致,資料庫可能無法有效使用該索引。
這也是為什麼在「依欄位 A 或欄位 B 篩選資料」的查詢情境中,不能只建立一個 (A, B) 的複合索引。因為查詢實際上只會使用其中一個條件,如果查詢只用到欄位B,這個(A, B)的複合索引就不會被用到。