登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL JSON_OVERLAPS 怎样判断数组有共同元素

来源:17golang原创

时间:2026-10-15 00:19:55 279浏览 收藏

我在做标签筛选时遇到过一个很容易写反的条件:用户选了一组标签,记录里也存了一组标签,我只想知道两组标签有没有至少一个共同值,却顺手写成了“左边是否包含右边全部元素”。这正是 JSON_OVERLAPS() 适合解决的场景。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html

对于两个 JSON 数组,JSON_OVERLAPS(a, b) 只要找到一个或多个相同数组元素就返回 1,完全没有共同元素时返回 0。它和 JSON_CONTAINS() 的差别可以先记成一句话:前者是“有交集”,后者更接近“候选集合全部被包含”。

先把“有一个”和“全都有”分开

这两个函数都能出现在 WHERE 条件里,但它们表达的业务关系不同。比如文章标签是 ["go", "mysql", "json"],用户筛选条件是 ["mysql", "redis"]:

  • JSON_OVERLAPS() 看到共同的 mysql,结果为 1。
  • JSON_CONTAINS() 如果要求左侧包含右侧整个数组,因为缺少 redis,结果为 0。

我通常先画出“集合关系”再决定函数,避免被函数名带偏。下面这张图是静态语义说明图,不是数据库运行截图。

JSON_OVERLAPS 与 JSON_CONTAINS 对数组交集和全部包含语义的静态结构说明图
图1:输入集合、匹配语义与 WHERE 条件的关系说明图,展示 JSON_OVERLAPS 的“共同元素”和 JSON_CONTAINS 的“全部元素”差异。

数组判断只需要一个共同元素

最小写法就是把两个合法 JSON 数组直接传给函数。下面的三个例子分别覆盖有交集、仍然有交集、完全无交集的情况:

-- 用数组交集判断是否至少命中一个标签
SELECT JSON_OVERLAPS('[1, 3, 5, 7]', '[2, 5, 7]') AS has_common;

-- 只要存在一个共同值,结果就是 1
SELECT JSON_OVERLAPS('[1, 3, 5, 7]', '[2, 6, 7]') AS has_common;

-- 没有共同数组元素时,结果是 0
SELECT JSON_OVERLAPS('[1, 3, 5, 7]', '[2, 6, 8]') AS has_common;

这里不需要先把数组拆成行再连接,也不需要手工拼接一串 OR。对业务来说,返回值只承担一个布尔判断:是否存在交集。

需要特别注意嵌套数组。JSON_OVERLAPS 比较的是数组元素,嵌套数组作为一个整体参与比较,不会因为内部出现部分数字就算命中。例如 [[1,2],[3,4],5] 与 [1,[2,3],[4,5]] 的外层元素没有完全相同的值,因此不能把它理解成递归查找。

对象、标量和 NULL 会改变你对结果的直觉

虽然标题关注数组,但实际写查询时很容易把 JSON 文档形态混在一起。JSON_OVERLAPS 的静态关系可以归纳为:数组看共同元素,对象看共同键值对,两个标量做相等比较;任一参数为 SQL NULL 时,结果也是 NULL。

-- 对象需要键和值同时相同,只有 d:10 这一对完全重合
SELECT JSON_OVERLAPS(
  '{"a": 1, "b": 10, "d": 10}',
  '{"c": 1, "e": 10, "f": 1, "d": 10}'
) AS object_overlap;

-- 两个标量相等时返回 1
SELECT JSON_OVERLAPS('5', '5') AS scalar_overlap;

-- SQL NULL 会让判断结果变成 NULL,而不是 0
SELECT JSON_OVERLAPS(NULL, '[1, 2]') AS null_result;

对象场景尤其容易误判:两个对象都出现键 a,但值分别为 1 和 5,这并不算共同键值对。换句话说,JSON_OVERLAPS 不是“只看 key 名称”。

数组和标量混用时,标量可以被当成一个数组元素参与比较,但 JSON 类型不会自动互转:

-- 数字 6 可以作为一个数组元素参与重叠判断
SELECT JSON_OVERLAPS('[4, 5, 6, 7]', '6') AS number_overlap;

-- 字符串 "6" 与数字 6 类型不同,不会自动转换后命中
SELECT JSON_OVERLAPS('[4, 5, "6", 7]', '6') AS string_number_boundary;
SELECT JSON_OVERLAPS('[4, 5, 6, 7]', '"6"') AS number_string_boundary;

放进 JSON 列的 WHERE 条件

真正的业务查询通常不是比较两个字面量,而是比较表里的 JSON 列和一个查询参数。假设 article.tags 保存文章标签数组,可以这样表达“至少命中一个用户筛选标签”:

-- tags 保存 JSON 数组,筛选条件只要求存在一个共同标签
SELECT id, title
FROM article
WHERE JSON_OVERLAPS(tags, '["mysql", "json"]');

如果筛选值来自应用参数,应让驱动或预处理语句负责绑定,而不是把用户输入直接拼进 SQL。这个例子只展示 JSON_OVERLAPS 的关系,不把参数转义细节伪装成固定字符串。

在 InnoDB 的 JSON 列上,官方文档还说明了:把 JSON_OVERLAPS 放在 WHERE 中时,可以朝多值索引方向优化。是否真的被采用,要结合列路径、索引定义、数据分布和执行计划判断,不能因为函数支持索引就默认每次查询都会变快。

JSON_OVERLAPS 对数组、对象、标量和 NULL 的静态比较边界说明图
图2:JSON 文档形态、重叠判断与特殊边界的关系说明图,帮助区分共同数组元素、共同键值对、标量相等和 NULL 结果。

什么时候不该用 JSON_OVERLAPS

我在回看旧查询时,通常会先问“业务要的是任意命中,还是全部满足”。如果要求候选数组中的每个元素都必须存在,应该优先考虑 JSON_CONTAINS();如果只是判断一个标量是否属于一个 JSON 数组,MEMBER OF() 的意图更直接。

业务问题更贴近的表达容易踩的边界
两组数组是否至少共享一个元素JSON_OVERLAPS()嵌套数组不做内部部分匹配
候选数组是否全部被目标数组包含JSON_CONTAINS()语义不是“有一个就算”
一个标量是否是数组成员MEMBER OF()数字与字符串类型要分清

如果数组元素本质上是需要排序、统计、去重和建立普通索引的业务实体,长期把它们塞在 JSON 中可能会让查询模型变复杂。JSON_OVERLAPS 解决的是“重叠判断”这一层,不会替代关系表建模。

我的判断清单

  1. 题目里出现“至少一个、任意一个、有交集”,先考虑 JSON_OVERLAPS()。
  2. 题目里出现“全部包含、每个条件都满足”,不要误用交集语义。
  3. 先确认 JSON 元素的类型,数字 6 和字符串 "6" 不要混看。
  4. 把 NULL 单独处理;三值逻辑下的 NULL 不是普通的“不相交”。
  5. 涉及大表时,再围绕 JSON 列路径、多值索引和执行计划做工程取舍。

最终可以把 JSON_OVERLAPS 记成一个很窄但很有用的判断:两个 JSON 文档有没有共同的可比较内容。对数组,它就是“是否存在共同元素”;只要把这个边界和 JSON_CONTAINS 的“全部包含”分开,筛选条件就不容易写反。

相关问题

  • JSON_OVERLAPS 能不能递归判断嵌套数组?不能把它当作递归部分匹配函数。
  • JSON_OVERLAPS 返回 NULL 时怎么筛选?根据业务决定是否用 COALESCE() 明确转换为 0。
  • JSON_OVERLAPS 和 MEMBER OF 有什么区别?前者比较两个 JSON 文档的重叠,后者更适合一个标量查一个数组。
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>