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

MySQL 怎样判断两个 JSON 数组是否有交集

来源:17golang原创

时间:2026-10-17 22:10:10 498浏览 收藏

MySQL 怎样判断两个 JSON 数组是否有交集

判断两个 JSON 数组是否至少共享一个元素,可以使用 JSON_OVERLAPS(array_a, array_b):有共同元素返回 1,没有返回 0,任一参数是 SQL NULL 时返回 NULL。它给出的是相交判断,不是交集元素列表。数字与字符串不能靠隐式转换凑成匹配,嵌套数组也不会被自动拍平后逐个比较。

下面以商品标签筛选为例:用户选择“收纳”或“照明”,商品只要拥有其中一个标签就应出现。场景为原创说明,不涉及真实线上事故;SQL 按 MySQL 8.4 文档编写,制作文章时未连接数据库执行。官方文档地址:https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html。

标签筛选漏命中,先确认要的是“任意一个”

假设某商品标签是“收纳、桌面”,用户选择“收纳、照明”。这个商品应该命中,因为共享“收纳”,不要求它同时具有“照明”。这时需要的是任意相交语义。把所有选中标签都必须包含的条件直接拿来用,容易让筛选结果意外变少。

-- 最小写法:两个 JSON_ARRAY 都产生真正的 JSON 数组。
SELECT JSON_OVERLAPS(
    JSON_ARRAY('收纳', '桌面'),
    JSON_ARRAY('收纳', '照明')
) AS has_overlap;

这一表达式的预期是 1。不要用 LIKE 搜索序列化后的数组文本:那比较的是字符片段,可能把标签子串、引号和嵌套内容都算进去,并没有表达数组元素相同的业务要求。

快速区分无交集、类型不符和未知值

遇到“肉眼相同却不命中”,先看元素类型。JSON 数字 208 与 JSON 字符串 "208" 是不同输入,不能把 JSON_OVERLAPS 当成自动清洗器。应在数据入口统一标签或编号的类型,而不是查询失败后把整份 JSON 转成字符串比较。

-- 数字与字符串不会因为显示相似而自动匹配,预期为 0。
SELECT JSON_OVERLAPS(JSON_ARRAY(208), JSON_ARRAY('208')) AS mixed_types;

-- 相同字符串标签可以匹配,预期为 1。
SELECT JSON_OVERLAPS(JSON_ARRAY('208'), JSON_ARRAY('208')) AS same_type;

-- SQL NULL 表示未知输入;这里预期返回 NULL,而不是 0。
SELECT JSON_OVERLAPS(NULL, JSON_ARRAY('收纳')) AS unknown_input;

-- 空数组没有可共享元素,预期为 0。
SELECT JSON_OVERLAPS(JSON_ARRAY(), JSON_ARRAY('收纳')) AS empty_input;

SQL NULL 与 JSON null 也不同。前者是 SQL 层的未知值,后者可以是 JSON 数组中的一个元素。若标签业务不允许 null 元素,应在写入契约中拒绝,而不是将它解释为“没有标签”。若业务明确规定未知输入也按不匹配处理,才在输出层使用 COALESCE;否则会丢失排查所需的未知状态。

JSON数组元素、类型约束与结果状态的静态关系图
图一:输入元素和结果状态分属不同边界;连线表达比较约束,不是 SQL 执行顺序或实测截图。

把条件放入查询,并单独检查嵌套元素

先用一份只读样例固定预期:记录 11 应命中,记录 12 不命中,记录 13 因未知标签返回 NULL。这样不需要创建表、修改数据或借用生产账号。

-- 只读样例:先保留每条记录的原始判断结果,方便看见 NULL。
WITH sample_items AS (
    SELECT 11 AS id, JSON_ARRAY('收纳', '桌面') AS tags
    UNION ALL
    SELECT 12, JSON_ARRAY('餐厨')
    UNION ALL
    SELECT 13, NULL
)
SELECT id, JSON_OVERLAPS(tags, JSON_ARRAY('收纳', '照明')) AS matched
FROM sample_items
ORDER BY id;

接入已有表时,假设 item_catalog.tags 是 JSON 标签数组,查询可以写成下面这样。表名和字段名是示例约定,不表示你的数据库已经存在这些对象。应用程序应绑定序列化后的 JSON 参数,不要拼接未经处理的用户文本。

-- 仅返回判断为 1 的记录;SQL NULL 不会被 WHERE 当成真。
SELECT id, tags
FROM item_catalog
WHERE JSON_OVERLAPS(tags, JSON_ARRAY('收纳', '照明')) = 1;

如果得到的数量仍不符合预期,再查看数组是否实际嵌套。一个子数组作为外层数组的元素时,需要按完整元素理解,不能把内部某个值相同当作整个子数组相同。

-- 左侧唯一元素是子数组 [208,309],右侧是数字 208;预期为 0。
SELECT JSON_OVERLAPS('[[208,309]]', '[208]') AS not_flattened;

-- 两侧共享完整子数组 [208,309],预期为 1。
SELECT JSON_OVERLAPS('[[208,309]]', '[[208,309]]') AS nested_equal;

如果业务真正要比较所有层级的叶子编号,就已经超出了当前数组元素比较的口径。不要只换一个函数名便声称覆盖递归数据;先定义需要比较的层级,再单独设计提取规则。

扁平与嵌套数组在元素粒度上的组成差异结构图
图二:外层元素可以是标量,也可以是整个子数组;该组成结构图说明为何内部部分重合不等于元素相同。

替换筛选条件后异常,怎样安全回退

本次修复应只涉及读取条件,不需要改写标签数据。发布前保留原查询和新查询的差异,并使用相同的固定样本比较返回 ID,而不只比较总数。如果新条件导致不符合业务口径的记录进入结果,先恢复旧谓词或关闭对应查询变更,再调查元素类型与选中条件的定义。

不要为了让样例命中就批量转换生产标签、删除 NULL 或展开全部嵌套结构;这会把查询语义调整变成数据迁移,回退难度明显增大。本文不提供这样的数据改写命令。若服务器不识别 JSON_OVERLAPS,先确认实际数据库产品与版本是否支持,不将 MySQL 8.4 的示例直接套到其他数据库。

结果恢复要看固定样本,不用告警消失代替判断

读者在自己的环境运行时,应分别确认共同字符串、无共同元素、数字与字符串、嵌套子数组、SQL NULL、空数组这几类输入。若用于线上筛选,观察既有错误率和查询耗时即可,不为这次修复虚构新的“性能提升比例”。

JSON_OVERLAPS 返回 1 只说明语义命中,不证明查询一定高效,也不证明自动走索引。这里不扩展索引设计;数据量较大时,应结合自己的表结构与执行计划另做性能评估,不能把一个正确的函数表达式当成通用优化结论。

把数组类型约定留在接口文档里

输入形态应如何解释本例判断口径
共享同一字符串标签至少一个元素相同1
完全不同的标签没有共享元素0
数字与同样字面的字符串类型不同,不自动转换0
SQL NULL输入未知NULL
空数组没有候选元素0

重复标签会让结果变成 2 吗?

不会。JSON_OVERLAPS 是布尔式相交判断,不统计共享元素数量;重复项不改变这个输出类型。

能直接得到交集列表吗?

不能。需要列表时,应另外定义展开、比较和去重规则;不要把返回 1 当成交集中的某个值。

为什么 WHERE 没返回 NULL 标签记录?

因为未知结果不满足“等于 1”。是否把未知当作不匹配,应由业务契约决定,而不是为了让数量一致而临时改数据。

这次标签筛选的关键,不是写出更长的 SQL,而是确认“任意相交”的定义,并把元素类型和层级约定写清。固定样本、只读比较和可回退的谓词变更,能让这个小功能保持清晰可维护。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>