问题描述
[{"key": "apples","string_values": ["red"]},{"key": "oranges","string_values": ["orange"]}]
有没有办法在字符串值上创建索引?我希望我能做这样的事情:
ALTER TABLE mytable ADD INDEX myindex(
(CAST(Metadata->'$[*].string_values' AS UNSIGNED ARRAY))
);
但是返回以下错误:无法在索引的标量键部分中存储数组或对象
小提琴例如:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ae36a5169094ae67a290febf09f49f0c
编辑:
以下选择现在将命中索引。
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->'$');
解决方法
有 2 个不同的问题。
首先是可以解决的。下一个小提琴描述它:
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"] |
dbfiddle here
第二个问题无法解决。
仔细查看您尝试创建索引时使用的语句:
ALTER TABLE mytable ADD INDEX myindex((CAST(metadata->'$[*].string_values' AS UNSIGNED ARRAY)));
数组 ["red","orange"]
中的值不是数字。
在回答之前,我应该做一个简短的免责声明。在 SQL 数据库中存储 JSON 数据实际上违背了基本的 SQL 设计理念。 SQL,顾名思义,用于结构化,即强类型数据。如果您将数据作为 JSON 传递到 SQL 数据库,您实际上是在丢弃其结构并将其视为字符串。
因此,在这种情况下,我会首先考虑以下两种选择:
(A) 我可以将这些数据视为结构化数据吗?在这种情况下,我将创建一个表,其中的列对应于 JSON 字段,然后像为任何其他列一样创建索引。
(B) 我的数据基本上是非结构化的吗?在这种情况下,我会尝试将其建模为 SQL 中的键值对,或者使用非 SQL 数据库,例如 CouchDB 或 Elasticsearch。
使用基于键值的方法,您可以将有问题的属性分离到一个单独的表中(对于将是键/索引/值的数组),然后您可以将其用于索引。这当然会增加一些开销,这取决于具体情况,在您的情况下可能会或可能不会被接受。
但是,仍然有很多合理的理由为什么某些东西可能不被归类为 A 或 B。如果您已经考虑并尝试了这两种方法,并且您可以清楚地证明为什么您的情况是例外的:
(C) 读取 the MySQL documentation about indexing generated columns
即便如此,您可能仍需要重构您的 JSON 数据,例如将数组转换为单个条目(id/index/value)。