#### 13.1.20.9 二级索引和生成的列

[](<>)

`InnoDB`支持虚拟生成列的二级索引。不支持其他索引类型。在虚拟列上定义的二级索引有时称为“虚拟索引”。

二级索引可以在一个或多个虚拟列或虚拟列和常规列的组合或存储的生成列上创建。包含虚拟列的二级索引可以定义为`独特`.

在虚拟生成列上创建二级索引时，生成的列值会在索引的记录中具体化。如果索引是[覆盖指数](glossary.html#glos_covering_index)（包括由查询检索到的所有列），生成的列值是从索引结构中的物化值中检索的，而不是“即时”计算的。

在虚拟列上使用二级索引时需要考虑额外的写入成本，因为在执行期间在二级索引记录中具体化虚拟列值时会执行计算[`插入`](insert.html)和[`更新`](update.html)操作。即使有额外的写入成本，虚拟列上的二级索引也可能比生成的更可取*存储*在聚集索引中具体化的列，从而导致需要更多磁盘空间和内存的更大表。如果未在虚拟列上定义二级索引，则读取会产生额外成本，因为每次检查列的行时都必须计算虚拟列值。

索引虚拟列的值是 MVCC 记录的，以避免在回滚或清除操作期间对生成的列值进行不必要的重新计算。记录值的数据长度受限于 767 字节的索引键限制`袖珍的`和`多余的`行格式和 3072 字节`动态的`和`压缩`行格式。

在虚拟列上添加或删除二级索引是就地操作。

##### 索引生成的列以提供 JSON 列索引

如其他地方所述，[`JSON`](json.html)列不能直接索引。要创建间接引用此类列的索引，您可以定义一个生成的列，该列提取应索引的信息，然后在生成的列上创建一个索引，如下例所示：

```
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.html)在一个[`选择`](select.html)或包含一个或多个表达式的其他 SQL 语句，这些表达式使用`->`要么`->>`运算符，这些表达式使用`JSON_EXTRACT()`和（如果需要）`JSON_UNQUOTE()`相反，如此处所示的输出[`显示警告`](show-warnings.html)紧随其后`解释`陈述：

```
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-search-functions.html#operator_json-column-path)和[`->>`](json-search-functions.html#operator_json-inline-path)运营商，以及那些[`JSON_EXTRACT()`](json-search-functions.html#function_json-extract)和[`JSON_UNQUOTE()`](json-modification-functions.html#function_json-unquote)功能，以获取更多信息和示例。

此技术还可用于提供间接引用无法直接索引的其他类型列的索引，例如`几何学`列。

在 MySQL 8.0.21 及更高版本中，还可以在[`JSON`](json.html)列使用[`JSON_VALUE()`](json-search-functions.html#function_json-value)带有表达式的函数，该表达式可用于优化使用该表达式的查询。有关更多信息和示例，请参阅该函数的描述。

###### NDB Cluster 中的 JSON 列和间接索引

[](<>) [](<>) [](<>) [](<>)也可以在 MySQL NDB Cluster 中使用 JSON 列的间接索引，但需要满足以下条件：

1.  [`新开发银行`](mysql-cluster.html)处理一个[`JSON`](json.html)列值在内部作为[`斑点`](blob.html).这意味着任何`新开发银行`具有一个或多个 JSON 列的表必须有一个主键，否则它不能记录在二进制日志中。

2.  这[`新开发银行`](mysql-cluster.html)存储引擎不支持虚拟列的索引。由于生成列的默认值是`虚拟的`，您必须明确指定要应用间接索引的生成列`已储存`.

    这**`创建表`**用于创建表的语句`杰普`这里显示的是一个版本`杰普`前面显示的表格，经过修改使其兼容`新开发银行`：


```
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.html)陈述：

```
INSERT INTO jempn (c) VALUES
  ('{"id": "1", "name": "Fred"}'),
  ('{"id": "2", "name": "Wilma"}'),
  ('{"id": "3", "name": "Barney"}'),
  ('{"id": "4", "name": "Betty"}');
```

现在`新开发银行`可以使用索引`一世`，如此处所示：

```
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)
```

您应该记住，存储生成的列使用[`数据存储器`](mysql-cluster-ndbd-definition.html#ndbparam-ndbd-datamemory)，并且此类列上的索引使用[`索引内存`](mysql-cluster-ndbd-definition.html#ndbparam-ndbd-indexmemory).
