繁体   English   中英

MySQL 5.7 - JSON 索引 - 生成具有非标量值的列

[英]MySQL 5.7 - JSON Indexing - Generated Columns with non-scalar values

我一直在研究 MySQL 5.7 中的 JSON 支持。 我对出于索引目的生成的列有几个问题。 https://dev.mysql.com/doc/refman/5.7/en/create-table.html#create-table-secondary-indexes-virtual-columns

具体参考这一行:

无法为 JSON 列编制索引。 您可以通过在从 JSON 列中提取量值的生成列上创建索引来解决此限制。

这对我来说似乎是一个很大的限制。 无论我在哪里,人们都建议使用生成的列。 但该解决方法适用于非常有限的一组用例。 或者,我理解错了。

搭建舞台

让我解释一下我的用例。 假设您有一个名为standards的表。 它具有以下结构:

CREATE TABLE `standards` (
  `id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `sections` json DEFAULT NULL,
  `subjects` json DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

sections列包含一个 JS 对象数组:

[
  {
    "id": 90491,
    "name": "A",
  },
  {
    "id": 90494,
    "name": "B",
  }
]

subjects列包含一个嵌套的 JS 对象:

{
  "576845": {
    "id": 576845,
    "name": "Computer Education"
  },
  "576848": {
    "id": 576848,
    "name": "English Language"
  },
  "576854": {
    "id": 576854,
    "name": "Environmental Science"
  },
  "576860": {
    "id": 576860,
    "name": "Mathematics"
  }
}

示例查询

查询 1

要查找section ID90494Standard记录,查询将是:

SELECT * from standards WHERE JSON_CONTAINS( sections->>'$[*].id', '90494' );

查询 2

要查找subject ID576854Standard记录,查询将是:

SELECT * from standards WHERE JSON_CONTAINS_PATH( subjects, 'one', '$."576854"');

要么

SELECT * from standards WHERE JSON_CONTAINS( subjects->>'$.*.id', '576854' );

问题

现在,以上所有的工作。 问题是查询执行全表扫描。

考虑上面的查询 1,我如何生成一个包含所有section IDs标量数据的虚拟列?

每个Standard记录都有多个sections ,具有多个 ID。 所以,我不能只创建一个整数虚拟列来存储单个值。 它必须是一个节 ID 数组,我们需要通过它进行搜索。

因此,我生成的列如下所示:

ALTER TABLE standards
ADD section_ids json GENERATED ALWAYS AS (sections->>'$[*].id') VIRTUAL NOT NULL;

生成的列现在将仅存储部分 ID 数组。 但是我不能在生成的列上添加索引,因为它又是一个 JSON 列。

问题 - 如何使用索引?

所以,问题归结为 - 对于上面显示的查询,我如何避免全表扫描?

任何建议,将不胜感激。

我不会说 MySQL 5.7 是不可能的——因为它有笨重的变通方法和限制——但我不会讨论如何使用该版本,因为它要困难得多,而且在许多方面都会有限制情况下,如果可以将大量项目添加到数组中,则可以达到。

但是,从现在支持多值索引的 MySQL 8.0.17开始是可能的。

ALTER TABLE standards
  ADD INDEX section_ids ( (CAST(sections->'$[*].id' AS UNSIGNED ARRAY)) ),
  ADD INDEX subject_ids ( (CAST(subjects->'$.*.id'AS UNSIGNED ARRAY)) );

** 请注意$.*将采用所有对象属性并返回每个格式化为数组的查询值 ( .id )。

EXPLAIN SELECT * from standards WHERE JSON_CONTAINS( sections->'$[*].id', '90494' );
EXPLAIN SELECT * from standards WHERE JSON_CONTAINS( subjects->'$.*.id', 576854 );

您将看到索引用于这些查询。

我会在旧版本中通过手动创建一个单独的索引表并使用触发器使其保持最新来解决这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM