InnoDB
支持虚拟生成列的二级索引。不支持其他索引类型。在虚拟列上定义的二级索引有时称为“虚拟索引”。
二级索引可以在一个或多个虚拟列上创建,或者在虚拟列和常规列或存储生成列的组合上创建。包含虚拟列的二级索引可以定义为UNIQUE
.
当在虚拟生成列上创建二级索引时,生成列值将具体化在索引的记录中。如果索引是 覆盖索引(包含查询检索到的所有列的索引),则生成的列值是从索引结构中的物化值中检索的,而不是“即时”计算的。
INSERT
由于在和
UPDATE
操作
期间在二级索引记录中具体化虚拟列值时执行的计算,在虚拟列上使用二级索引时需要考虑额外的写入成本。即使有额外的写入成本,虚拟列上的二级索引也可能比生成的存储列更可取,后者在聚集索引中具体化,从而导致需要更多磁盘空间和内存的更大表。如果未在虚拟列上定义二级索引,则会产生额外的读取成本,因为每次检查列的行时都必须计算虚拟列值。
索引虚拟列的值记录在 MVCC 中,以避免在回滚或清除操作期间对生成的列值进行不必要的重新计算。记录值的数据长度受索引键限制,行格式为 767 字节
,
COMPACT
行REDUNDANT
格式为 3072 字节。
DYNAMIC
COMPRESSED
在虚拟列上添加或删除二级索引是就地操作。
正如别处所指出的,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)
(我们包装了本例中最后一条语句的输出以适合查看区域。)
当您EXPLAIN
在
SELECT
包含一个或多个使用
->
or运算符的表达式的 SQL 语句或其他 SQL 语句上使用时,这些表达式将使用and(如果需要)->>
转换为它们的等价物,如紧接此
语句
后的输出所示:JSON_EXTRACT()
JSON_UNQUOTE()
SHOW
WARNINGS
EXPLAIN
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)
有关其他信息和示例,
请参阅
->
and
->>
运算符以及
JSON_EXTRACT()
and
函数的描述。JSON_UNQUOTE()
该技术还可用于提供索引,这些索引间接引用不能直接索引的其他类型的列,例如GEOMETRY
列。
在 MySQL 8.0.21 及更高版本中,还可以JSON
使用带有表达式的函数在列上创建索引,JSON_VALUE()
该表达式可用于优化使用该表达式的查询。有关更多信息和示例,请参阅该函数的描述。
NDB Cluster 中的 JSON 列和间接索引
也可以在 MySQL NDB Cluster 中使用 JSON 列的间接索引,但要满足以下条件:
CREATE TABLE
用于创建此处显示的表
的语句jempn
是先前显示的表的一个版本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
可以使用 index 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
.