当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL JSON_TABLE 嵌套数组为空时为什么还会返回一行

MySQL JSON_TABLE 嵌套数组为空时为什么还会返回一行

来源:17golang原创 2026-10-22 05:32:21 0浏览 收藏

使用 JSON_TABLE() 展开订单明细时,如果某个对象的 items 是空数组,结果里仍然出现一行、但明细列全是 NULL,通常不是 MySQL 多返回了数据,而是 NESTED PATH 为父级记录保留的补位行。要只留下真实明细,应先标记数组是否有元素,再在外层过滤;ON EMPTY 主要处理路径取不到值时的列值,不负责取消这行。

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

判断这类结果时,先把“父级对象是否保留”和“嵌套数组是否匹配”分开看:根路径命中了对象,嵌套路径没有命中元素,MySQL 就可能保留父级并用 NULL 补齐嵌套列。

为什么 NESTED PATH 会补出一行

JSON_TABLE() 把 JSON 文档投影成关系表。根路径 $[*] 负责找到每个对象,根级列例如 order_id 直接从对象取值;NESTED PATH '$.items[*]' 再尝试把对象内的数组展开成多行。

关键在于两层路径承担的责任不同。只要根路径找到订单对象,父级记录就有存在理由;当 items[*] 没有元素时,嵌套列没有可填入的值,于是会形成一行 NULL 补位。它表达的是“这个订单存在,但当前没有可展开的明细”,不是一条 sku 为空的真实商品记录。

JSON_TABLE 根路径、NESTED PATH 与 NULL 补位行的静态结构关系说明图
图1:结构说明图,展示 JSON_TABLE 的根级列与 NESTED PATH 嵌套列之间的补位关系;这是静态说明图,不是运行截图。
-- 用一条有明细、 一条空数组的订单区分两种输入结构。
SET @orders = JSON_ARRAY(
  JSON_OBJECT('order_id', 1001, 'items', JSON_ARRAY(
    JSON_OBJECT('sku', 'A-01', 'qty', 2)
  )),
  JSON_OBJECT('order_id', 1002, 'items', JSON_ARRAY())
);

-- 根级列保留订单号,NESTED PATH 负责展开 items 数组。
SELECT jt.order_id, jt.sku, jt.qty
FROM JSON_TABLE(
  @orders,
  '$[*]' COLUMNS(
    order_id INT PATH '$.order_id',
    NESTED PATH '$.items[*]' COLUMNS(
      sku VARCHAR(30) PATH '$.sku',
      qty INT PATH '$.qty'
    )
  )
) AS jt;

这个查询的关注点不是具体输出排版,而是行的来源:1001 有真实明细,1002 的数组没有元素,嵌套列没有对应值。排查时不要仅凭 sku IS NULL 就断言数据坏了,因为合法的明细也可能允许 sku 为空。

用 EXISTS PATH 标记空数组

如果业务需要同时保留订单概览和明细查询,可以在根级列中增加一个存在性标记。EXISTS PATH 返回路径是否能找到匹配内容,标记字段会和 JSON_TABLE 行集一起输出。这样筛选条件不必借用业务列的 NULL 语义。

-- has_items 只表达 items[*] 是否有可展开的数组元素。
SELECT jt.order_id, jt.has_items, jt.sku, jt.qty
FROM JSON_TABLE(
  @orders,
  '$[*]' COLUMNS(
    order_id INT PATH '$.order_id',
    has_items TINYINT EXISTS PATH '$.items[*]',
    NESTED PATH '$.items[*]' COLUMNS(
      sku VARCHAR(30) PATH '$.sku',
      qty INT PATH '$.qty'
    )
  )
) AS jt;

这里的 has_items 是结构判断,不是库存数量,也不是对 qty 的校验。空数组的订单可以显示为 0,有元素的订单显示为 1;如果你还要保留订单级统计,就不要在最内层盲目过滤。

在外层筛掉补位行

只需要真实明细时,可以把 JSON_TABLE 放进派生表或公共表表达式,再用标记过滤。把条件写在外层的好处是:先完成 JSON 到关系列的映射,再明确表达“结果集不要空数组补位行”。

EXISTS PATH 标记与外层 WHERE 过滤真实明细结果的静态关系说明图
图2:关系说明图,展示 EXISTS PATH 标记如何与外层过滤条件配合;这是静态说明图,不是运行截图。
-- 先生成结构标记,再在外层只选择有真实明细的行。
SELECT x.order_id, x.sku, x.qty
FROM (
  SELECT jt.order_id, jt.has_items, jt.sku, jt.qty
  FROM JSON_TABLE(
    @orders,
    '$[*]' COLUMNS(
      order_id INT PATH '$.order_id',
      has_items TINYINT EXISTS PATH '$.items[*]',
      NESTED PATH '$.items[*]' COLUMNS(
        sku VARCHAR(30) PATH '$.sku',
        qty INT PATH '$.qty'
      )
    )
  ) AS jt
) AS x
-- 过滤结构标记,不把合法的 NULL 业务字段当成空数组。
WHERE x.has_items = 1;

如果一条明细允许 sku 为空,这种写法比 WHERE sku IS NOT NULL 更稳。如果你的输入保证 sku 永远非空,后者也能工作,但应把这个前提写进数据约束或接口约定,而不是让查询读者猜。

ON EMPTY 处理的是列值,不是行数

ON EMPTY 用于路径没有找到值时的列行为,常见选择是默认置 NULL、提供默认 JSON 值,或在缺失时抛错。它解决的是“列怎么填”,而 NESTED PATH 解决的是“嵌套结构如何映射”。两者放在同一条语句里,不代表设置了默认值就会删除补位行。

因此排查可以按三个问题拆开:第一,根路径是否找到父级对象;第二,嵌套路径是否找到数组元素;第三,命中后的字段缺失时应该使用 NULL、默认值还是错误。只有第三个问题属于 ON EMPTY 的直接职责。

现象更可能的原因处理方向
父级列有值,明细列全是 NULLNESTED PATH 没有匹配元素增加 EXISTS PATH,按结构标记过滤
明细行存在但某个字段为 NULL字段路径缺失或值本来就是 NULL针对列设置 ON EMPTY 或保留 NULL 语义
缺失字段直接报错列定义使用了 ERROR ON EMPTY确认接口契约,再决定默认值或错误策略

常见问题

空数组补位行算不算重复数据?

通常不算。它是父级对象与嵌套路径之间的结构结果;是否保留取决于你是在做订单概览,还是只要明细行。

能不能直接用 sku IS NOT NULL?

可以,但前提是业务明确禁止真实明细的 sku 为空。更通用的做法是使用 EXISTS PATH 产生结构标记,再按标记筛选。

ON EMPTY 能让空数组不生成行吗?

不能把它当作行过滤开关。它主要影响匹配行里的列值;要控制结果集,应使用结构标记或外层查询条件。

记住一个判断顺序:先看根对象是否命中,再看嵌套数组有没有元素,最后才处理字段缺失的默认值。这样遇到 JSON_TABLE 返回 NULL 明细时,就能快速区分补位行和真实脏数据。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 循环里 defer 为什么造成资源积压Go 循环里 defer 为什么造成资源积压
上一篇
Go 循环里 defer 为什么造成资源积压
MySQL CTE 拆分复杂查询 怎样处理
下一篇
MySQL CTE 拆分复杂查询 怎样处理
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    583次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    621次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    655次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    622次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    436次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码