結構化查詢語言/索引管理
索引是所有 SQL 資料庫的關鍵特性。它們提供對資料的快速訪問。因此,幾乎所有實現都支援 CREATE INDEX 語句。
然而,CREATE INDEX 語句不是 SQL 標準的一部分!其原因尚不清楚。可能是刻意避免所有實現問題。或者,它源於供應商實現的各種語法和缺乏找到妥協方案。
在本頁,我們提供了一些關於索引的基本概念以及大多數實現中通用的語法。
CREATE [UNIQUE] INDEX <index_name> ON <table_name> (<column_name> [, <column_name>]);
DBMS 提供對儲存在表中的資料的快速訪問。人們可能會認為這種高速訪問是由於現代計算機的快速硬體:每秒數百萬個 CPU 週期,毫秒級的 I/O 速率,微秒或納秒級的 RAM 訪問等等。這是事實,但只是一部分。相反,智慧軟體演算法的使用,特別是在處理大量資料時,是主要因素。
考慮向 DBMS 發出請求,以確定是否可以在包含 100 萬個條目的表中找到一個特定姓名的人。使用原始的線性演算法,系統必須讀取平均 50 萬行才能決定問題。該二分查詢演算法實現了一種更復雜的策略,該策略在讀取 20 行或更少行後即可回答問題。在這種情況下,這種演算法選擇導致效能提升 25000 倍。為了真正理解這種改進的程度,您可能需要將您的薪水乘以 25000。
誠然,線性訪問和二分查詢演算法之間的這種比較有點簡單。首先,DBMS 通常讀取包含多行而不是單行的塊。但這並沒有改變情況。如果一個塊包含 100 行,將上面的例子從 100 萬行修改為 1 億行。其次,二分查詢演算法假設資料是有序的。這意味著在資料輸入期間,需要額外的步驟將實際輸入排序到現有資料中。這僅應用一次,並且與讀取訪問次數無關。總之,在資料輸入期間有額外的工作,而在資料訪問期間有更少的工作。這取決於資料的典型使用,以確定額外的工作是否值得。
索引是一個額外的儲存,它儲存從表中的原始資料複製或推匯出的資料。它只包含冗餘資料。索引包含哪些部分?在二分查詢策略的常見情況下,索引儲存表列的原始值以及指向原始行的反向引用。在大多數情況下,索引被組織成一個平衡樹,其中列的值作為樹的鍵,反向引用作為每個鍵的附加資訊。
二分查詢演算法只是構建索引的眾多選項之一。索引的共同特徵是:它們僅包含冗餘資訊;在 CPU 週期、RAM 或磁碟空間方面使用額外的資源;併為大型資料集上的查詢提供更好的效能。在小表或包含多個索引的表的情況下,索引的缺點(效能或資源使用方面)可能超過使用索引的好處。
如果一個應用程式根據某個條件檢索資料 - 例如,電話簿應用程式中的個人姓名 - 並且該條件包含表的列,則該列應該有索引。
CREATE INDEX person_lastname_idx ON person(lastname);
索引有它自己的可自由選擇的名稱 - 例如person_lastname_idx - 並且是在特定表中的特定列上構建的。索引可以在 CREATE TABLE 語句之後直接定義和建立(當表中沒有資料時)或者在一些或大量 INSERT 命令之後建立。建立之後,DBMS 應該處於能夠比以前更快地回答以下問題的狀態。
SELECT count(*)
FROM person
WHERE lastname = 'Miller';
索引可以在評估 WHERE 子句期間使用。DBMS 可以選擇:一方面 - 讀取所有person行並計算 lastname 為 'Miller' 的行;另一方面 - 讀取索引(可能使用二分查詢)並計算所有值為 'Miller' 的節點。使用哪種策略取決於很多決定。例如,如果 DBMS 知道大約 30% 的行包含 'Miller',它可能會選擇與知道只有 0.3% 的行包含 'Miller' 時不同的策略。
一個表可以有多個索引。
CREATE INDEX person_firstname_idx ON person(firstname);
在這種情況下,以下查詢會發生什麼?
SELECT count(*)
FROM person
WHERE lastname = 'Miller'
AND firstname = 'Henry';
同樣,DBMS 有不止一種選擇來檢索預期的結果。它可以使用兩個索引中的一個,讀取結果行並查詢缺少的另一個值。或者它讀取兩個索引並計算共同的反向引用。或者它忽略兩個索引,讀取資料並計算兩個條件都適用的行。如前所述,這取決於很多決定。
如果一個應用程式通常在一個查詢中搜索兩列,例如姓名和姓氏,則為這兩列構建一個索引可能很有用。這種策略與上面的例子有很大不同,在上面的例子中,我們為每列構建了兩個獨立的索引。
CREATE INDEX person_fullname_idx ON person(lastname, firstname);
在這種情況下,平衡樹的鍵是姓氏和姓名的串聯。DBMS 可以使用此索引來處理查詢姓名和姓名的查詢。它還可以使用此索引來處理僅查詢姓氏的查詢。但它不能在僅查詢姓名的查詢中使用此索引。姓名可以在平衡樹的不同位置出現。因此,它對這種查詢毫無用處。
在某些情況下,現有索引不能用於查詢基礎列。假設對姓名的查詢應該是不區分大小寫的。為此,應用程式將所有使用者輸入轉換為大寫並使用 UPPER() 函式來處理範圍內的列。
-- Original user input was: 'miller'
SELECT count(*)
FROM person
WHERE UPPER(lastname) = 'MILLER';
由於 WHERE 子句中的條件只查詢大寫字元,並且索引是以區分大小寫的方式構建的,因此平衡樹中的鍵毫無用處:'miller' 排序的位置與 'Miller' 大不相同。為了解決這個問題,可以定義一個索引,它使用與 WHERE 條件完全相同的策略。
CREATE INDEX person_uppername_idx ON person(UPPER(lastname)); -- not supported by MySQL
現在,'UPPER()' 查詢可以使用這種所謂的函式索引。
每個表的 主鍵都是唯一的,這意味著沒有兩行可以包含相同的值。有時,一列或幾列的串聯也是唯一的。要確保此條件,您可以定義一個唯一約束,或者您可以定義一個帶有額外唯一條件的索引。(通常,唯一約束在後臺默默地使用唯一索引。)
CREATE UNIQUE INDEX person_lastname_unique_idx ON person(lastname);
唯一索引只能在現有資料上建立,如果範圍內的列確實只包含唯一的值(這在我們的資料庫示例中並非如此)。
可以使用以下命令刪除索引
DROP INDEX <index_name>;