InnoDB
支持虚拟生成列的二级索引。不支持其他索引类型。在虚拟列上定义的二级索引有时被称为 “虚拟索引”。
可以在一个或多个虚拟列上创建二级索引,也可以在虚拟列和常规列或存储的生成列的组合上创建二级索引。包含虚拟列的二级索引可以定义为 UNIQUE
。
当在虚拟生成列上创建二级索引时,生成列的值会在索引记录中进行物化。如果索引是 覆盖索引(包含查询检索的所有列的索引),则会从索引结构中的物化值中检索生成列的值,而不是 “动态” 计算。
在虚拟列上使用辅助索引时,由于在INSERT
和UPDATE
操作期间,将虚拟列值物化到辅助索引记录中,需要考虑额外的写入成本。即使有额外的写入成本,虚拟列上的辅助索引可能优于生成的存储列,因为生成的存储列是在聚簇索引中物化的,这会导致表更大,需要更多磁盘空间和内存。如果在虚拟列上没有定义辅助索引,则读取会有额外的成本,因为每次检查列的行时都需要计算虚拟列值。
索引虚拟列的值将被 MVCC 记录,以避免在回滚或清除操作期间不必要地重新计算生成的列值。记录值的长度受索引键限制,对于 COMPACT
和 REDUNDANT
行格式,限制为 767 字节;对于 DYNAMIC
和 COMPRESSED
行格式,限制为 3072 字节。
在虚拟列上添加或删除辅助索引是一个就地操作。
如其他地方所述,JSON
列无法直接索引。要创建间接引用此类列的索引,可以定义一个生成的列来提取要索引的信息,然后在生成的列上创建索引,如本例所示
mysql> CREATE TABLE jemp (
-> c JSON,
-> g INT GENERATED ALWAYS AS (c->"$.id"),
-> INDEX i (g)
-> );
Query OK, 0 rows affected (0.28 sec)
mysql> INSERT INTO jemp (c) VALUES
> ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'),
> ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');
Query OK, 4 rows affected (0.04 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT c->>"$.name" AS name
> FROM jemp WHERE g > 2;
+--------+
| name |
+--------+
| Barney |
| Betty |
+--------+
2 rows in set (0.00 sec)
mysql> EXPLAIN SELECT c->>"$.name" AS name
> FROM jemp WHERE g > 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: jemp
partitions: NULL
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 2
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name'))
AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2)
1 row in set (0.00 sec)
(我们将本例中最后一个语句的输出进行了包装,以适应查看区域。)
当您对包含一个或多个使用 ->
或 ->>
运算符的表达式的 SELECT
或其他 SQL 语句使用 EXPLAIN
时,这些表达式将被转换为使用 JSON_EXTRACT()
和(如果需要)JSON_UNQUOTE()
的等效表达式,如下所示,此 EXPLAIN
语句之后的 SHOW WARNINGS
语句的输出
mysql> EXPLAIN SELECT c->>"$.name"
> FROM jemp WHERE g > 2 ORDER BY c->"$.name"\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: jemp
partitions: NULL
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 2
filtered: 100.00
Extra: Using where; Using filesort
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name')) AS
`c->>"$.name"` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) order by
json_extract(`test`.`jemp`.`c`,'$.name')
1 row in set (0.00 sec)
有关更多信息和示例,请参阅 ->
和 ->>
运算符的描述,以及 JSON_EXTRACT()
和 JSON_UNQUOTE()
函数的描述。
此技术还可用于提供间接引用其他类型列的索引,这些列无法直接索引,例如 GEOMETRY
列。
也可以使用 JSON_VALUE()
函数在 JSON
列上创建索引,该函数使用表达式,该表达式可用于优化使用该表达式的查询。有关更多信息和示例,请参阅该函数的描述。
NDB Cluster 中的 JSON 列和间接索引
在 MySQL NDB Cluster 中也可以使用 JSON 列的间接索引,但需满足以下条件
用于创建此处所示的 jempn
表的 CREATE TABLE
语句是先前显示的 jemp
表的版本,其修改使其与 NDB
兼容
CREATE TABLE jempn (
a BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
c JSON DEFAULT NULL,
g INT GENERATED ALWAYS AS (c->"$.id") STORED,
INDEX i (g)
) ENGINE=NDB;
我们可以使用以下 INSERT
语句填充此表
INSERT INTO jempn (c) VALUES
('{"id": "1", "name": "Fred"}'),
('{"id": "2", "name": "Wilma"}'),
('{"id": "3", "name": "Barney"}'),
('{"id": "4", "name": "Betty"}');
现在,NDB
可以使用索引 i
,如下所示
mysql> EXPLAIN SELECT c->>"$.name" AS name
-> FROM jempn WHERE g > 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: jempn
partitions: p0,p1,p2,p3
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 3
filtered: 100.00
Extra: Using pushed condition (`test`.`jempn`.`g` > 2)
1 row in set, 1 warning (0.01 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select
json_unquote(json_extract(`test`.`jempn`.`c`,'$.name')) AS `name` from
`test`.`jempn` where (`test`.`jempn`.`g` > 2)
1 row in set (0.00 sec)
请记住,存储的生成列以及此类列上的任何索引都使用 DataMemory
。