如何使用 MySQL 8 在 JSON 数组上创建索引
How to create an index on a JSON array using MySQL 8
我在 mysql 8 中有一个 JSON 字段,如下所示:
[{"key": "apples", "string_values": ["red"]}, {"key": "oranges", "string_values": ["orange"]}]
有没有办法为字符串值创建索引?我希望我能做这样的事情:
ALTER TABLE mytable ADD INDEX myindex(
(CAST(metadata->'$[*].string_values' AS UNSIGNED ARRAY))
);
但是returns出现以下错误:无法在索引的标量键部分存储数组或对象
Fiddle 例如:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ae36a5169094ae67a290febf09f49f0c
编辑:
我开始认为我能做到这一点的唯一方法是使用生成的列:
下面的select现在将命中指数
ALTER TABLE mytable ADD string_values JSON AS (JSON_EXTRACT(metadata, '$[*].string_values[0]'));
ALTER TABLE mytable ADD INDEX idx_string_values(
(CAST(string_values->'$' AS CHAR(100) ARRAY))
);
SELECT * FROM mytable WHERE 'red' MEMBER OF(string_values->'$');
在回答之前我应该做一个简短的免责声明。在 SQL 数据库中存储 JSON 数据实际上违背了基本的 SQL 设计理念。 SQL,顾名思义,用于结构化,即强类型数据。如果您将数据作为 JSON 传递给 SQL 数据库,您实际上是在丢弃其结构并将其视为字符串。
因此,遇到这种情况我会首先考虑以下两种方案:
(A) 我可以将此数据视为结构化数据吗?在这种情况下,我会创建一个 table,其中的列对应于 JSON 字段,然后像其他任何列一样创建一个索引。
(B) 我的数据基本上 是非结构化的吗?在这种情况下,我会尝试将其建模为 SQL 中的键值对,或者使用非 SQL 数据库,例如 CouchDB 或 Elasticsearch。
使用基于键值的方法,您可以将有问题的 属性 分成单独的 table(对于数组 key/index/value),然后您可以将其用于索引。这当然会增加一些开销,根据具体情况,在您的情况下可能会或可能不会接受table。
但是,仍然有很多合理的理由可以说明某些东西可能无法归类为 A 或 B。如果您已经考虑并尝试过这两种方法,并且您可以清楚地证明您的案例是例外的:
(C) 阅读 the MySQL documentation about indexing generated columns
即便如此,您可能仍需要重构 JSON 数据,例如将数组转换为单独的条目 (id/index/value)。
有 2 个不同的问题。
首先是可以解决的。下一个fiddle描述一下:
CREATE TABLE mytable (metadata JSON);
INSERT INTO mytable VALUES ('[{"key": "apples", "string_values": ["red"]}, {"key": "oranges", "string_values": ["orange"]}]');
-- you try to index arrays
SELECT metadata->'$[*].string_values' FROM mytable;
-- but you need in scalar values
SELECT metadata->'$[*].string_values[0]' FROM mytable;
| metadata->'$[*].string_values' |
| :----------------------------- |
| [["red"], ["orange"]] |
| metadata->'$[*].string_values[0]' |
| :-------------------------------- |
| ["red", "orange"] |
db<>fiddle here
第二题无解
仔细查看您尝试创建索引时使用的语句:
ALTER TABLE mytable ADD INDEX myindex((CAST(metadata->'$[*].string_values' AS UNSIGNED ARRAY))) ;
数组 ["red", "orange"]
中的值不是数字。
我在 mysql 8 中有一个 JSON 字段,如下所示:
[{"key": "apples", "string_values": ["red"]}, {"key": "oranges", "string_values": ["orange"]}]
有没有办法为字符串值创建索引?我希望我能做这样的事情:
ALTER TABLE mytable ADD INDEX myindex(
(CAST(metadata->'$[*].string_values' AS UNSIGNED ARRAY))
);
但是returns出现以下错误:无法在索引的标量键部分存储数组或对象
Fiddle 例如:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ae36a5169094ae67a290febf09f49f0c
编辑:
我开始认为我能做到这一点的唯一方法是使用生成的列:
下面的select现在将命中指数
ALTER TABLE mytable ADD string_values JSON AS (JSON_EXTRACT(metadata, '$[*].string_values[0]'));
ALTER TABLE mytable ADD INDEX idx_string_values(
(CAST(string_values->'$' AS CHAR(100) ARRAY))
);
SELECT * FROM mytable WHERE 'red' MEMBER OF(string_values->'$');
在回答之前我应该做一个简短的免责声明。在 SQL 数据库中存储 JSON 数据实际上违背了基本的 SQL 设计理念。 SQL,顾名思义,用于结构化,即强类型数据。如果您将数据作为 JSON 传递给 SQL 数据库,您实际上是在丢弃其结构并将其视为字符串。
因此,遇到这种情况我会首先考虑以下两种方案:
(A) 我可以将此数据视为结构化数据吗?在这种情况下,我会创建一个 table,其中的列对应于 JSON 字段,然后像其他任何列一样创建一个索引。
(B) 我的数据基本上 是非结构化的吗?在这种情况下,我会尝试将其建模为 SQL 中的键值对,或者使用非 SQL 数据库,例如 CouchDB 或 Elasticsearch。
使用基于键值的方法,您可以将有问题的 属性 分成单独的 table(对于数组 key/index/value),然后您可以将其用于索引。这当然会增加一些开销,根据具体情况,在您的情况下可能会或可能不会接受table。
但是,仍然有很多合理的理由可以说明某些东西可能无法归类为 A 或 B。如果您已经考虑并尝试过这两种方法,并且您可以清楚地证明您的案例是例外的:
(C) 阅读 the MySQL documentation about indexing generated columns
即便如此,您可能仍需要重构 JSON 数据,例如将数组转换为单独的条目 (id/index/value)。
有 2 个不同的问题。
首先是可以解决的。下一个fiddle描述一下:
CREATE TABLE mytable (metadata JSON); INSERT INTO mytable VALUES ('[{"key": "apples", "string_values": ["red"]}, {"key": "oranges", "string_values": ["orange"]}]');
-- you try to index arrays SELECT metadata->'$[*].string_values' FROM mytable; -- but you need in scalar values SELECT metadata->'$[*].string_values[0]' FROM mytable;
| metadata->'$[*].string_values' | | :----------------------------- | | [["red"], ["orange"]] | | metadata->'$[*].string_values[0]' | | :-------------------------------- | | ["red", "orange"] |
db<>fiddle here
第二题无解
仔细查看您尝试创建索引时使用的语句:
ALTER TABLE mytable ADD INDEX myindex((CAST(metadata->'$[*].string_values' AS UNSIGNED ARRAY))) ;
数组 ["red", "orange"]
中的值不是数字。