CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
[index_type]
ON tbl_name (key_part,...)
[index_option]
[algorithm_option | lock_option] ...
key_part: {col_name [(length)] | (expr)} [ASC | DESC]
index_option: {
KEY_BLOCK_SIZE [=] value
| index_type
| WITH PARSER parser_name
| COMMENT 'string'
| {VISIBLE | INVISIBLE}
| ENGINE_ATTRIBUTE [=] 'string'
| SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
}
index_type:
USING {BTREE | HASH}
algorithm_option:
ALGORITHM [=] {DEFAULT | INPLACE | COPY}
lock_option:
LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
通常,您會在建立資料表的同時,使用 CREATE TABLE
在資料表上建立所有索引。請參閱章節 15.1.20,「CREATE TABLE 陳述式」。此指南對於 InnoDB
資料表尤其重要,其中主索引鍵決定了資料檔案中資料列的實體配置。CREATE INDEX
可讓您將索引新增至現有的資料表。
CREATE INDEX
會對應到 ALTER TABLE
陳述式來建立索引。請參閱 章節 15.1.9,「ALTER TABLE 陳述式」。CREATE INDEX
不能用於建立 PRIMARY KEY
;請改用 ALTER TABLE
。如需有關索引的詳細資訊,請參閱章節 10.3.1,「MySQL 如何使用索引」。
InnoDB
支援虛擬資料行上的次要索引。如需詳細資訊,請參閱 章節 15.1.20.9,「次要索引和產生的資料行」。
當 innodb_stats_persistent
設定啟用時,在該資料表上建立索引之後,請針對 InnoDB
資料表執行 ANALYZE TABLE
陳述式。
key_part
規格的 expr
也可以採用 (CAST
的形式,以在 json_expression
AS type
ARRAY)JSON
資料行上建立多值索引。請參閱 多值索引。
採用 (
形式的索引規格會建立具有多個索引鍵部分的索引。索引鍵值是透過串連給定索引鍵部分的值來形成的。例如,key_part1
, key_part2
, ...)(col1, col2, col3)
指定一個多欄索引,其索引鍵由 col1
、col2
和 col3
的值組成。
key_part
規格可以 ASC
或 DESC
結尾,以指定索引值是以遞增或遞減順序儲存。如果未提供順序指定符,則預設值為遞增。
ASC
和 DESC
不支援 HASH
索引、多值索引或 SPATIAL
索引。
以下章節描述 CREATE INDEX
陳述式的不同方面
對於字串資料行,可以使用
語法來指定索引前置長度,以建立僅使用資料行值的前導部分的索引col_name
(length
)
前置詞必須針對
BLOB
和TEXT
索引鍵部分指定。此外,BLOB
和TEXT
資料行只能針對InnoDB
、MyISAM
和BLACKHOLE
資料表建立索引。前置詞限制以位元組為單位測量。但是,
CREATE TABLE
、ALTER TABLE
和CREATE INDEX
陳述式中索引規格的前置詞長度,對於非二進位字串類型 (CHAR
、VARCHAR
、TEXT
) 會解譯為字元數,而對於二進位字串類型 (BINARY
、VARBINARY
、BLOB
) 則會解譯為位元組數。在為使用多位元組字元集的非二進位字串資料行指定前置詞長度時,請將此納入考量。前置詞支援和前置詞長度 (如果支援) 取決於儲存引擎。例如,對於使用
REDUNDANT
或COMPACT
列格式的InnoDB
資料表,前置詞最多可為 767 個位元組長。對於使用DYNAMIC
或COMPRESSED
列格式的InnoDB
資料表,前置詞長度限制為 3072 個位元組。對於MyISAM
資料表,前置詞長度限制為 1000 個位元組。NDB
儲存引擎不支援前置詞 (請參閱 章節 25.2.7.6,「NDB Cluster 中不支援或缺少的功能」)。
如果指定的索引前置詞超出最大資料行資料類型大小,CREATE INDEX
會以下列方式處理索引
對於非唯一索引,若啟用嚴格 SQL 模式,則會發生錯誤;若未啟用嚴格 SQL 模式,則會將索引長度縮減為最大資料行資料類型大小內,並產生警告。
對於唯一索引,無論 SQL 模式為何,都會發生錯誤,因為縮減索引長度可能會啟用插入不符合指定唯一性需求之非唯一項目。
此處顯示的陳述式會使用 name
資料行的前 10 個字元建立索引 (假設 name
具有非二進位字串類型)
CREATE INDEX part_of_name ON customer (name(10));
如果資料行中的名稱通常在前 10 個字元中不同,則使用此索引執行的查閱速度不應比使用從整個 name
資料行建立的索引慢很多。此外,為索引使用資料行前置詞可以使索引檔案小得多,這可以節省大量磁碟空間,並且也可能會加快 INSERT
作業。
「一般」索引會為資料行值或資料行值的前置詞建立索引。例如,在下表中,給定 t1
資料列的索引項目包含完整的 col1
值和由前 10 個字元組成的 col2
值的前置詞
CREATE TABLE t1 (
col1 VARCHAR(10),
col2 VARCHAR(20),
INDEX (col1, col2(10))
);
索引運算式值的功能索引鍵部分也可以用來取代資料行或資料行前置詞值。使用功能索引鍵部分可以為未直接儲存在資料表中的值建立索引。範例
CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1))));
CREATE INDEX idx1 ON t1 ((col1 + col2));
CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1);
ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);
具有多個索引鍵部分的索引可以混合使用非功能性和功能性索引鍵部分。
功能索引鍵部分支援 ASC
和 DESC
。
功能索引鍵部分必須遵守下列規則。如果索引鍵部分定義包含不允許的建構,則會發生錯誤。
在索引定義中,將運算式括在括號中,以將其與資料行或資料行前置詞區分開來。例如,允許這樣做;運算式會括在括號中
INDEX ((col1 + col2), (col3 - col4))
這會產生錯誤;運算式不會括在括號中
INDEX (col1 + col2, col3 - col4)
功能索引鍵部分不能僅包含資料行名稱。例如,不允許這樣做
INDEX ((col1), (col2))
請改為將索引鍵部分寫為不使用括號的非功能性索引鍵部分
INDEX (col1, col2)
功能索引鍵部分運算式不能參考資料行前置詞。如需解決方法,請參閱本章節稍後有關
SUBSTRING()
和CAST()
的討論。外鍵規格中不允許使用功能索引鍵部分。
對於 CREATE TABLE ... LIKE
,目標資料表會保留原始資料表中的功能索引鍵部分。
功能索引是以隱藏的虛擬產生欄實作的,這有以下含義:
每個功能鍵部分都會計入表格欄總數的限制;請參閱第 10.4.7 節,「表格欄數與列大小的限制」。
功能鍵部分會繼承所有適用於產生欄的限制。例如:
只有允許用於產生欄的功能才能用於功能鍵部分。
不允許使用子查詢、參數、變數、儲存函式和可載入函式。
有關適用限制的更多資訊,請參閱第 15.1.20.8 節,「CREATE TABLE 和產生欄」和第 15.1.9.2 節,「ALTER TABLE 和產生欄」。
虛擬產生欄本身不需要儲存空間。索引本身會像其他索引一樣佔用儲存空間。
包含功能鍵部分的索引支援 UNIQUE
。但是,主鍵不能包含功能鍵部分。主鍵需要儲存產生欄,但功能鍵部分是以虛擬產生欄實作,而不是儲存產生欄。
SPATIAL
和 FULLTEXT
索引不能有功能鍵部分。
如果表格不包含主鍵,InnoDB
會自動將第一個 UNIQUE NOT NULL
索引提升為主要索引。這不支援具有功能鍵部分的 UNIQUE NOT NULL
索引。
如果存在重複索引,非功能索引會發出警告。包含功能鍵部分的索引不具有此功能。
若要移除功能鍵部分參照的欄,必須先移除索引。否則會發生錯誤。
雖然非功能鍵部分支援前綴長度規範,但功能鍵部分無法這樣做。解決方案是使用 SUBSTRING()
(或 CAST()
,如本節稍後所述)。為了在查詢中使用包含 SUBSTRING()
函數的功能鍵部分,WHERE
子句必須包含具有相同引數的 SUBSTRING()
。在以下範例中,只有第二個 SELECT
能夠使用索引,因為那是唯一引數與索引規範匹配的 SUBSTRING()
查詢
CREATE TABLE tbl (
col1 LONGTEXT,
INDEX idx1 ((SUBSTRING(col1, 1, 10)))
);
SELECT * FROM tbl WHERE SUBSTRING(col1, 1, 9) = '123456789';
SELECT * FROM tbl WHERE SUBSTRING(col1, 1, 10) = '1234567890';
功能鍵部分能夠對否則無法索引的值進行索引,例如 JSON
值。但是,必須正確執行此操作才能達到預期效果。例如,此語法無效:
CREATE TABLE employees (
data JSON,
INDEX ((data->>'$.name'))
);
此語法失敗的原因是:
->>
運算子會轉換為JSON_UNQUOTE(JSON_EXTRACT(...))
。JSON_UNQUOTE()
會傳回資料類型為LONGTEXT
的值,因此隱藏的產生欄會被指派相同的資料類型。MySQL 無法對金鑰部分上未指定前綴長度的
LONGTEXT
欄進行索引,並且功能鍵部分不允許使用前綴長度。
若要對 JSON
欄建立索引,您可以嘗試使用 CAST()
函數,如下所示:
CREATE TABLE employees (
data JSON,
INDEX ((CAST(data->>'$.name' AS CHAR(30))))
);
隱藏的產生欄會被指派 VARCHAR(30)
資料類型,這可以建立索引。但是,當嘗試使用索引時,此方法會產生新的問題:
CAST()
會傳回具有定序utf8mb4_0900_ai_ci
(伺服器預設定序) 的字串。JSON_UNQUOTE()
會傳回具有定序utf8mb4_bin
(硬式編碼) 的字串。
結果,先前表格定義中的索引運算式與下列查詢中的 WHERE
子句運算式之間存在定序不符的情況:
SELECT * FROM employees WHERE data->>'$.name' = 'James';
由於查詢和索引中的運算式不同,因此不會使用索引。為了支援功能鍵部分的這種情況,最佳化工具會在尋找要使用的索引時自動剝除 CAST()
,但是 僅在 索引運算式的定序與查詢運算式的定序相符時才這麼做。若要使用具有功能鍵部分的索引,以下兩種解決方案都有效(儘管它們的效果略有不同):
解決方案 1。指派索引運算式與
JSON_UNQUOTE()
相同的定序:CREATE TABLE employees ( data JSON, INDEX idx ((CAST(data->>"$.name" AS CHAR(30)) COLLATE utf8mb4_bin)) ); INSERT INTO employees VALUES ('{ "name": "james", "salary": 9000 }'), ('{ "name": "James", "salary": 10000 }'), ('{ "name": "Mary", "salary": 12000 }'), ('{ "name": "Peter", "salary": 8000 }'); SELECT * FROM employees WHERE data->>'$.name' = 'James';
->>
運算子與JSON_UNQUOTE(JSON_EXTRACT(...))
相同,而JSON_UNQUOTE()
會傳回具有定序utf8mb4_bin
的字串。因此,比較會區分大小寫,且只有一列符合:+------------------------------------+ | data | +------------------------------------+ | {"name": "James", "salary": 10000} | +------------------------------------+
解決方案 2。在查詢中指定完整運算式:
CREATE TABLE employees ( data JSON, INDEX idx ((CAST(data->>"$.name" AS CHAR(30)))) ); INSERT INTO employees VALUES ('{ "name": "james", "salary": 9000 }'), ('{ "name": "James", "salary": 10000 }'), ('{ "name": "Mary", "salary": 12000 }'), ('{ "name": "Peter", "salary": 8000 }'); SELECT * FROM employees WHERE CAST(data->>'$.name' AS CHAR(30)) = 'James';
CAST()
會傳回具有定序utf8mb4_0900_ai_ci
的字串,因此比較不區分大小寫,且有兩列符合:+------------------------------------+ | data | +------------------------------------+ | {"name": "james", "salary": 9000} | | {"name": "James", "salary": 10000} | +------------------------------------+
請注意,雖然最佳化工具支援自動剝除具有索引產生欄的 CAST()
,但以下方法無效,因為它在有和沒有索引的情況下會產生不同的結果 (錯誤編號 #27337092):
mysql> CREATE TABLE employees (
data JSON,
generated_col VARCHAR(30) AS (CAST(data->>'$.name' AS CHAR(30)))
);
Query OK, 0 rows affected, 1 warning (0.03 sec)
mysql> INSERT INTO employees (data)
VALUES ('{"name": "james"}'), ('{"name": "James"}');
Query OK, 2 rows affected, 1 warning (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 1
mysql> SELECT * FROM employees WHERE data->>'$.name' = 'James';
+-------------------+---------------+
| data | generated_col |
+-------------------+---------------+
| {"name": "James"} | James |
+-------------------+---------------+
1 row in set (0.00 sec)
mysql> ALTER TABLE employees ADD INDEX idx (generated_col);
Query OK, 0 rows affected, 1 warning (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> SELECT * FROM employees WHERE data->>'$.name' = 'James';
+-------------------+---------------+
| data | generated_col |
+-------------------+---------------+
| {"name": "james"} | james |
| {"name": "James"} | James |
+-------------------+---------------+
2 rows in set (0.01 sec)
UNIQUE
索引會建立一個約束,使得索引中的所有值都必須是不同的。如果您嘗試新增具有與現有列匹配的索引鍵值的新列,則會發生錯誤。如果您在 UNIQUE
索引中為欄指定前綴值,則欄值必須在前綴長度內是唯一的。UNIQUE
索引允許可以包含 NULL
的欄有多個 NULL
值。
如果表格具有由具有整數類型的單一欄組成的 PRIMARY KEY
或 UNIQUE NOT NULL
索引,則您可以使用 _rowid
在 SELECT
陳述式中參照索引的欄,如下所示:
如果存在由單一整數欄組成的
PRIMARY KEY
,則_rowid
會參照PRIMARY KEY
欄。如果存在PRIMARY KEY
,但它不是由單一整數欄組成,則無法使用_rowid
。否則,如果第一個
UNIQUE NOT NULL
索引由單一整數欄組成,則_rowid
會參照該索引中的欄。如果第一個UNIQUE NOT NULL
索引不是由單一整數欄組成,則無法使用_rowid
。
FULLTEXT
索引僅支援 InnoDB
和 MyISAM
表格,並且只能包含 CHAR
、VARCHAR
和 TEXT
欄。索引始終對整個欄進行;不支援欄前綴索引,並且如果指定任何前綴長度,則會忽略它。如需操作詳細資訊,請參閱第 14.9 節,「全文搜尋函數」。
InnoDB
支援多值索引。多值索引是在儲存值陣列的欄上定義的次要索引。「正常」索引對於每個資料記錄都有一個索引記錄 (1:1)。多值索引對於單一資料記錄可以有多個索引記錄 (N:1)。多值索引用於索引 JSON
陣列。例如,在下列 JSON 文件中的郵遞區號陣列上定義的多值索引會為每個郵遞區號建立索引記錄,而每個索引記錄都會參照相同的資料記錄。
{
"user":"Bob",
"user_id":31,
"zipcode":[94477,94536]
}
建立多值索引
您可以在 CREATE TABLE
、ALTER TABLE
或 CREATE INDEX
陳述式中建立多值索引。這需要在索引定義中使用 CAST(... AS ... ARRAY)
,這會將 JSON
陣列中相同類型的純量值轉換為 SQL 資料類型陣列。然後,會使用 SQL 資料類型陣列中的值以透明方式產生虛擬欄;最後,會在虛擬欄上建立功能索引 (也稱為虛擬索引)。在 SQL 資料類型陣列中的值的虛擬欄上定義的功能索引會形成多值索引。
以下清單中的範例會顯示三種不同的方法,可以在名為 customers
的表格中,在 JSON
欄 custinfo
的陣列 $.zipcode
上建立多值索引 zips
。在每種情況下,JSON 陣列都會轉換為 UNSIGNED
整數值的 SQL 資料類型陣列。
僅限
CREATE TABLE
CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, custinfo JSON, INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) ) );
CREATE TABLE
加ALTER TABLE
CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, custinfo JSON ); ALTER TABLE customers ADD INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
CREATE TABLE
加CREATE INDEX
CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, custinfo JSON ); CREATE INDEX zips ON customers ( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
多值索引也可以定義為複合索引的一部分。此範例顯示包含兩個單值部分 (針對 id
和 modified
欄) 和一個多值部分 (針對 custinfo
欄) 的複合索引:
CREATE TABLE customers (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
custinfo JSON
);
ALTER TABLE customers ADD INDEX comp(id, modified,
(CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
在複合索引中只能使用一個多值索引鍵部分。多值索引鍵部分可以相對於索引鍵的其他部分以任何順序使用。換句話說,剛剛顯示的 ALTER TABLE
陳述式可以使用 comp(id, (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY), modified))
(或任何其他排序),並且仍然有效。
使用多值索引
當 WHERE
子句中指定下列函數時,最佳化工具會使用多值索引來擷取記錄:
我們可以透過建立和填入 customers
資料表來示範這一點,使用以下 CREATE TABLE
和 INSERT
陳述式:
mysql> CREATE TABLE customers (
-> id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-> custinfo JSON
-> );
Query OK, 0 rows affected (0.51 sec)
mysql> INSERT INTO customers VALUES
-> (NULL, NOW(), '{"user":"Jack","user_id":37,"zipcode":[94582,94536]}'),
-> (NULL, NOW(), '{"user":"Jill","user_id":22,"zipcode":[94568,94507,94582]}'),
-> (NULL, NOW(), '{"user":"Bob","user_id":31,"zipcode":[94477,94507]}'),
-> (NULL, NOW(), '{"user":"Mary","user_id":72,"zipcode":[94536]}'),
-> (NULL, NOW(), '{"user":"Ted","user_id":56,"zipcode":[94507,94582]}');
Query OK, 5 rows affected (0.07 sec)
Records: 5 Duplicates: 0 Warnings: 0
首先,我們在 customers
資料表上執行三個查詢,分別使用 MEMBER OF()
、JSON_CONTAINS()
和 JSON_OVERLAPS()
,每個查詢的結果如下所示:
mysql> SELECT * FROM customers
-> WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
+----+---------------------+-------------------------------------------------------------------+
| id | modified | custinfo |
+----+---------------------+-------------------------------------------------------------------+
| 2 | 2019-06-29 22:23:12 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
| 3 | 2019-06-29 22:23:12 | {"user": "Bob", "user_id": 31, "zipcode": [94477, 94507]} |
| 5 | 2019-06-29 22:23:12 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]} |
+----+---------------------+-------------------------------------------------------------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM customers
-> WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+---------------------+-------------------------------------------------------------------+
| id | modified | custinfo |
+----+---------------------+-------------------------------------------------------------------+
| 2 | 2019-06-29 22:23:12 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
| 5 | 2019-06-29 22:23:12 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]} |
+----+---------------------+-------------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM customers
-> WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+---------------------+-------------------------------------------------------------------+
| id | modified | custinfo |
+----+---------------------+-------------------------------------------------------------------+
| 1 | 2019-06-29 22:23:12 | {"user": "Jack", "user_id": 37, "zipcode": [94582, 94536]} |
| 2 | 2019-06-29 22:23:12 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
| 3 | 2019-06-29 22:23:12 | {"user": "Bob", "user_id": 31, "zipcode": [94477, 94507]} |
| 5 | 2019-06-29 22:23:12 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]} |
+----+---------------------+-------------------------------------------------------------------+
4 rows in set (0.00 sec)
接下來,我們在先前的三個查詢的每一個查詢上執行 EXPLAIN
。
mysql> EXPLAIN SELECT * FROM customers
-> WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
剛顯示的三個查詢都無法使用任何鍵。為了解決這個問題,我們可以在 JSON
欄位 (custinfo
) 中的 zipcode
陣列上新增一個多值索引,如下所示:
mysql> ALTER TABLE customers
-> ADD INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
Query OK, 0 rows affected (0.47 sec)
Records: 0 Duplicates: 0 Warnings: 0
當我們再次執行先前的 EXPLAIN
陳述式時,我們現在可以觀察到查詢可以(而且確實)使用剛建立的索引 zips
。
mysql> EXPLAIN SELECT * FROM customers
-> WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
+----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ref | zips | zips | 9 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | range | zips | zips | 9 | NULL | 6 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | range | zips | zips | 9 | NULL | 6 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
多值索引可以定義為唯一鍵。如果定義為唯一鍵,嘗試插入多值索引中已存在的值會傳回重複鍵錯誤。如果已存在重複值,嘗試新增唯一多值索引將會失敗,如下所示:
mysql> ALTER TABLE customers DROP INDEX zips;
Query OK, 0 rows affected (0.55 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE customers
-> ADD UNIQUE INDEX zips((CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)));
ERROR 1062 (23000): Duplicate entry '[94507, ' for key 'customers.zips'
mysql> ALTER TABLE customers
-> ADD INDEX zips((CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)));
Query OK, 0 rows affected (0.36 sec)
Records: 0 Duplicates: 0 Warnings: 0
多值索引的特性
多值索引具有下列額外特性:
影響多值索引的 DML 操作的處理方式,與影響一般索引的 DML 操作相同,唯一的區別在於,單一叢集索引記錄可能有多個插入或更新。
空值性和多值索引
如果多值鍵部分具有空陣列,則不會將任何項目新增至索引,並且資料記錄無法透過索引掃描存取。
如果多值鍵部分產生傳回
NULL
值,則會將包含NULL
的單一項目新增至多值索引。如果鍵部分定義為NOT NULL
,則會回報錯誤。如果將類型化的陣列欄位設定為
NULL
,則儲存引擎會儲存一個包含NULL
的單一記錄,該記錄指向資料記錄。索引陣列中不允許
JSON
空值。如果傳回的任何值為NULL
,則會將其視為 JSON 空值,並回報 無效的 JSON 值 錯誤。
由於多值索引是虛擬欄位上的虛擬索引,因此它們必須遵守與虛擬產生欄位上的次要索引相同的規則。
不會為空陣列新增索引記錄。
多值索引的限制與約束
多值索引受限於此處列出的限制和約束:
每個多值索引僅允許一個多值鍵部分。不過,
CAST(... AS ... ARRAY)
運算式可以參考JSON
文件中的多個陣列,如下所示:CAST(data->'$.arr[*][*]' AS UNSIGNED ARRAY)
在此情況下,所有符合 JSON 運算式的值都會以單一平面陣列的形式儲存在索引中。
具有多值鍵部分的索引不支援排序,因此不能用作主鍵。基於相同原因,多值索引不能使用
ASC
或DESC
關鍵字定義。多值索引不能是涵蓋索引。
每個記錄的多值索引最大值數量取決於可以在單一復原日誌頁面上儲存的資料量,即 65221 位元組(64K 減去 315 位元組的額外負荷),這表示索引鍵值的最大總長度也是 65221 位元組。索引鍵的最大數量取決於各種因素,這會阻止定義特定的限制。測試顯示,多值索引允許每個記錄多達 1604 個整數索引鍵。當達到限制時,將會回報類似以下的錯誤:錯誤 3905 (HY000):超過多值索引 'idx' 每個記錄的最大值數量,超出 1 個值。
多值鍵部分中允許的唯一運算式類型是
JSON
運算式。此運算式不需要參考插入索引欄位的 JSON 文件中的現有元素,但它本身必須在語法上有效。由於同一叢集索引記錄的索引記錄分散在整個多值索引中,因此多值索引不支援範圍掃描或僅索引掃描。
多值索引在外部索引鍵規格中不被允許。
無法為多值索引定義索引前置碼。
不支援線上建立多值索引,這表示操作使用
ALGORITHM=COPY
。請參閱 效能和空間需求。除了以下兩種字元集和校對組合之外,多值索引不支援其他字元集和校對:
具有預設
binary
校對的binary
字元集具有預設
utf8mb4_0900_as_cs
校對的utf8mb4
字元集。
與
InnoDB
資料表的欄位上的其他索引一樣,無法使用USING HASH
建立多值索引;嘗試這樣做會導致警告:此儲存引擎不支援 HASH 索引演算法,因此改用儲存引擎預設值。(照常支援USING BTREE
。)
MyISAM
、InnoDB
、NDB
和 ARCHIVE
儲存引擎支援空間欄位,例如 POINT
和 GEOMETRY
。(第 13.4 節「空間資料類型」描述了空間資料類型。)不過,空間欄位索引的支援在各引擎之間有所不同。空間欄位上的空間和非空間索引根據以下規則提供。
空間欄位上的空間索引具有下列特性:
空間欄位上的非空間索引(使用 INDEX
、UNIQUE
或 PRIMARY KEY
建立)具有下列特性:
在索引鍵部分清單之後,可以提供索引選項。index_option
值可以是下列任一值:
KEY_BLOCK_SIZE [=]
value
對於
MyISAM
資料表,KEY_BLOCK_SIZE
會選擇性地指定用於索引鍵區塊的大小 (以位元組為單位)。該值會被視為提示;如有必要,可能會使用不同的大小。為個別索引定義指定的KEY_BLOCK_SIZE
值會覆寫資料表層級的KEY_BLOCK_SIZE
值。在
InnoDB
資料表的索引層級上不支援KEY_BLOCK_SIZE
。請參閱 第 15.1.20 節「CREATE TABLE 陳述式」。index_type
某些儲存引擎允許您在建立索引時指定索引類型。例如:
CREATE TABLE lookup (id INT) ENGINE = MEMORY; CREATE INDEX id_index ON lookup (id) USING BTREE;
表 15.1,「每個儲存引擎的索引類型」顯示不同儲存引擎支援的允許索引類型值。在列出多個索引類型的情況下,第一個是未提供索引類型指定子時的預設值。表格中未列出的儲存引擎不支援索引定義中的
index_type
子句。不能將
index_type
子句用於FULLTEXT INDEX
規格。全文索引實作取決於儲存引擎。空間索引實作為 R 樹索引。如果您指定對給定儲存引擎無效的索引類型,但存在引擎可以使用而不會影響查詢結果的另一個索引類型,則引擎會使用可用的類型。剖析器會將
RTREE
識別為類型名稱。僅允許將其用於SPATIAL
索引。BTREE
索引由NDB
儲存引擎實作為 T 樹索引。注意對於
NDB
資料表欄位上的索引,只有唯一索引或主鍵才能指定USING
選項。USING HASH
會阻止建立已排序索引;否則,在NDB
資料表上建立唯一索引或主鍵會自動導致同時建立已排序索引和雜湊索引,每個索引都會對同一組欄位建立索引。對於包含一個或多個
NULL
欄位的NDB
表格的唯一索引,雜湊索引只能用於查找字面值,這表示IS [NOT] NULL
條件需要完整掃描表格。一個解決方法是確保在這種表格上使用一個或多個NULL
欄位建立唯一索引時,始終包含排序索引;也就是說,建立索引時避免使用USING HASH
。如果您指定了一個對於給定儲存引擎無效的索引類型,但有另一個引擎可以使用且不會影響查詢結果的索引類型可用,則引擎會使用可用的類型。解析器會將
RTREE
識別為類型名稱,但目前無法為任何儲存引擎指定此類型。注意在
ON
子句之前使用tbl_name
index_type
選項已被棄用;預期未來 MySQL 版本將移除對此位置使用該選項的支援。如果較早和較晚位置都給定了index_type
選項,則以最後一個選項為準。TYPE
被識別為type_name
USING
的同義詞。但是,type_name
USING
是建議使用的形式。下表顯示支援
index_type
選項的儲存引擎的索引特性。表 15.2 InnoDB 儲存引擎索引特性
索引類別 索引類型 儲存 NULL 值 允許存在多個 NULL 值 IS NULL 掃描類型 IS NOT NULL 掃描類型 主鍵 BTREE
否 否 不適用 不適用 唯一 BTREE
是 是 索引 索引 鍵 BTREE
是 是 索引 索引 全文檢索
不適用 是 是 表 表 空間
不適用 否 否 不適用 不適用
表 15.3 MyISAM 儲存引擎索引特性
索引類別 索引類型 儲存 NULL 值 允許存在多個 NULL 值 IS NULL 掃描類型 IS NOT NULL 掃描類型 主鍵 BTREE
否 否 不適用 不適用 唯一 BTREE
是 是 索引 索引 鍵 BTREE
是 是 索引 索引 全文檢索
不適用 是 是 表 表 空間
不適用 否 否 不適用 不適用
表 15.4 MEMORY 儲存引擎索引特性
索引類別 索引類型 儲存 NULL 值 允許存在多個 NULL 值 IS NULL 掃描類型 IS NOT NULL 掃描類型 主鍵 BTREE
否 否 不適用 不適用 唯一 BTREE
是 是 索引 索引 鍵 BTREE
是 是 索引 索引 主鍵 雜湊
否 否 不適用 不適用 唯一 雜湊
是 是 索引 索引 鍵 雜湊
是 是 索引 索引
表 15.5 NDB 儲存引擎索引特性
索引類別 索引類型 儲存 NULL 值 允許存在多個 NULL 值 IS NULL 掃描類型 IS NOT NULL 掃描類型 主鍵 BTREE
否 否 索引 索引 唯一 BTREE
是 是 索引 索引 鍵 BTREE
是 是 索引 索引 主鍵 雜湊
否 否 表格 (請參閱註 1) 表格 (請參閱註 1) 唯一 雜湊
是 是 表格 (請參閱註 1) 表格 (請參閱註 1) 鍵 雜湊
是 是 表格 (請參閱註 1) 表格 (請參閱註 1) 表格註解
1.
USING HASH
會阻止建立隱含的排序索引。WITH PARSER
parser_name
此選項只能用於
FULLTEXT
索引。如果全文索引和搜尋操作需要特殊處理,它會將解析器外掛程式與索引關聯。InnoDB
和MyISAM
支援全文解析器外掛程式。如果您的MyISAM
表格有相關聯的全文解析器外掛程式,您可以使用ALTER TABLE
將表格轉換為InnoDB
。如需更多資訊,請參閱 全文解析器外掛程式 和 編寫全文解析器外掛程式。COMMENT '
string
'索引定義可以包含最多 1024 個字元的選用註解。
可以使用
CREATE INDEX
陳述式的index_option
COMMENT
子句,為個別索引設定索引頁面的MERGE_THRESHOLD
。例如CREATE TABLE t1 (id INT); CREATE INDEX id_index ON t1 (id) COMMENT 'MERGE_THRESHOLD=40';
當刪除列或透過更新操作縮短列時,如果索引頁面的頁面填滿百分比低於
MERGE_THRESHOLD
值,InnoDB
會嘗試將索引頁面與相鄰的索引頁面合併。預設的MERGE_THRESHOLD
值為 50,這也是先前硬編碼的值。也可以使用
CREATE TABLE
和ALTER TABLE
陳述式,在索引層級和表格層級定義MERGE_THRESHOLD
。如需更多資訊,請參閱 第 17.8.11 節「設定索引頁面的合併臨界值」。VISIBLE
、INVISIBLE
指定索引的可見性。預設情況下,索引是可見的。最佳化器不會使用不可見的索引。索引可見性的規格適用於主鍵 (顯式或隱式) 以外的索引。如需更多資訊,請參閱 第 10.3.12 節「不可見索引」。
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
用於指定主要和次要儲存引擎的索引屬性。這些選項保留供未來使用。指定給此選項的值是一個字串常值,其中包含有效的 JSON 文件或空字串 ('')。無效的 JSON 會被拒絕。
CREATE INDEX i1 ON t1 (c1) ENGINE_ATTRIBUTE='{"key":"value"}';
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值可以重複,不會出錯。在這種情況下,會使用最後指定的值。伺服器不會檢查
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值,當表格的儲存引擎變更時也不會清除這些值。
可以指定 ALGORITHM
和 LOCK
子句來影響表格複製方法,以及在修改索引時讀寫表格的並行程度。它們的意義與 ALTER TABLE
陳述式相同。如需更多資訊,請參閱 第 15.1.9 節「ALTER TABLE 陳述式」。
NDB Cluster 使用與標準 MySQL 伺服器相同的 ALGORITHM=INPLACE
語法支援線上作業。如需更多資訊,請參閱 第 25.6.12 節「NDB Cluster 中使用 ALTER TABLE 進行線上作業」。