SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN ...
在这样的表达式或函数中使用列会破坏查询使用索引来帮助优化查询的任何机会。上面显示的查询被迫进行表扫描。
关于“高效访问”的说法具有误导性。这意味着在查询检查包含 JSON 文档的行后,它可以提取字段,而无需解析 JSON 语法的文本。但仍然需要表扫描来搜索行。换句话说,查询必须检查每一行。
打个比方,如果我在电话簿中搜索名字为“Bill”的人,我仍然需要阅读电话簿中的每一页,即使名字已突出显示,以便更快地找到他们。
MySQL 5.7允许您在表中定义虚拟列,然后在虚拟列上创建索引。
ALTER TABLE t1
ADD COLUMN series AS (JSON_EXTRACT(data, '$.series')),
ADD INDEX (series);
然后,如果您查询虚拟列,它可以使用索引并避免表扫描。
SELECT * FROM t1
WHERE series IN ...
这很好,但它有点忽略了使用 JSON 的意义。使用 JSON 的吸引力在于它允许您添加新属性而无需执行 ALTER TABLE。但事实证明,如果您想借助索引搜索 JSON 字段,则无论如何都必须定义一个额外的(虚拟)列。
但您不必为其定义虚拟列和索引everyJSON 文档中的字段 - 仅那些您想要搜索或排序的字段。 JSON 中可能还有其他属性,您只需在选择列表中提取这些属性,如下所示:
SELECT JSON_EXTRACT(data, '$.series') AS series FROM t1
WHERE <other conditions>
我通常会说这是在 MySQL 中使用 JSON 的最佳方式。仅在选择列表中。
当您引用其他子句(JOIN、WHERE、GROUP BY、HAVING、ORDER BY)中的列时,使用常规列(而不是 JSON 文档中的字段)会更有效。
我做了一个演讲,叫做如何在 MySQL 中错误地使用 JSON https://www.slideshare.net/billkarwin/how-to-use-json-in-mysql-wrong在 2018 年 4 月的 Percona Live 会议上。我将在秋季更新并重复在 Oracle Code One 上的演讲。
JSON 还有其他问题。例如,在我的测试中,与存储相同数据的传统列相比,JSON 文档需要的存储空间是传统列的 2-3 倍。
MySQL 正在积极推广其新的 JSON 功能,主要是为了阻止人们迁移到 MongoDB。但像 MongoDB 这样的面向文档的数据存储从根本上来说是一种非关系型数据组织方式。它与关系型不同。我并不是说一个比另一个更好,它只是一种不同的技术,适合不同类型的查询。
当 JSON 使您的查询更加高效时,您应该选择使用 JSON。
不要仅仅因为一项技术新颖或为了时尚而选择它。
编辑:如果您的 WHERE 子句使用与虚拟列定义完全相同的表达式,则 MySQL 中的虚拟列实现应该使用索引。也就是说,下面的should使用虚拟列上的索引,因为虚拟列已定义AS (JSON_EXTRACT(data,"$.series"))
SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN ...
但我通过测试此功能发现,如果表达式是 JSON 提取函数,则由于某种原因它不起作用。它适用于其他类型的表达式,但不适用于 JSON 函数。更新:据报道,这最终在 MySQL 5.7.33 中有效。