必须用JSON_TABLE将JSON数组展开为多行后再统计;直接COUNT或JSON_LENGTH无法准确获取嵌套数组元素个数,且不同数据库展开机制差异大。

MySQL 8.0 怎么用 JSON_TABLE 展开 JSON 数组并统计元素个数
直接说结论:必须用 JSON_TABLE 把数组“炸开”成行,再配合 COUNT 或 GROUP BY 统计。原生 JSON 字段不能直接 COUNT(json_column->'$[*]')——那只会返回整个数组,不是元素个数。
常见错误现象:COUNT(json_column) 返回 1(整条记录数),JSON_LENGTH(json_column) 看似能用,但它只适用于**顶层是数组**的 JSON;如果字段里存的是 {"tags": ["a","b"]} 这种对象,JSON_LENGTH(json_column) 就返回 1(对象键数),不是 tags 里的元素数。
- 先确认 JSON 结构:用
SELECT json_column->'$.tags' FROM t LIMIT 1看实际路径和类型 - 确保 MySQL 版本 ≥ 8.0.4(
JSON_TABLE是 8.0.4 引入) -
JSON_TABLE的PATH必须指向数组,比如'$.tags',不能写'$'或'$.tags[*]'(后者语法错误) - 别漏掉
COLUMNS子句里的FOR ORDINALITY或PATH,否则展开失败或字段为空
示例:统计每条记录中 tags 数组的元素数量
SELECT id, COUNT(*) AS tag_count FROM t, JSON_TABLE(tags, '$[*]' COLUMNS (val TEXT PATH '$')) AS jt GROUP BY id;
PostgreSQL 怎么用 jsonb_array_elements() 展开并聚合
PostgreSQL 更直接:jsonb_array_elements() 是核心函数,它把 jsonb 数组转成多行 jsonb 值,之后就能正常 COUNT、GROUP BY、甚至 JOIN。
容易踩的坑:jsonb_array_elements() 只接受 jsonb 类型,传 text 或 json 会报错 function jsonb_array_elements(text) does not exist;另外,如果字段可能为 NULL 或非数组(比如是对象或字符串),函数会直接报错或静默跳过——得提前过滤或用 jsonb_typeof() 判断。
- 强制转
jsonb:col::jsonb,但确保内容合法,否则报错 - 安全展开:加条件
WHERE jsonb_typeof(col) = 'array' - 如果要统计嵌套结构(如
{"data": [{"x":1},{"x":2}]}),先用col->'data'提取,再传给jsonb_array_elements() - 性能注意:对大表频繁展开可能触发全表扫描,考虑加生成列 + 索引(如
ALTER TABLE t ADD COLUMN tag_count INT GENERATED ALWAYS AS (jsonb_array_length(col)) STORED)
示例:统计所有记录中 items 数组总元素数
SELECT COUNT(*) FROM t, jsonb_array_elements(items::jsonb);
SQLite 怎么处理 JSON 数组统计(无原生 JSON_TABLE)
SQLite 3.38+ 内置了 JSON 函数,但没有类似 JSON_TABLE 的横向展开能力。想统计数组元素,只能靠 json_each() + 递归或子查询模拟,而且仅限顶层数组——它不支持路径表达式,只能遍历整个 JSON 对象/数组的直接子项。
典型问题:json_each(json_col) 对 ["a","b","c"] 会返回三行,但 key 是 0/1/2,value 是 "a"/"b"/"c";可用来计数,但没法直接关联原记录的其他字段,除非用 LATERAL(SQLite 3.39+ 支持)或子查询包装。
- 必须开启 JSON 扩展:
SELECT load_extension('mod_json');(部分编译版本默认启用) -
json_each()输入必须是 JSON 文本,不是字符串字面量;json_each('["a","b"]')可以,json_each(col)要求col存的是合法 JSON 字符串 - 无法处理深层嵌套(如
$.data.list[*]),只能一层展开 - 替代方案:应用层解析,或升级到支持
json_tree()(3.42+)的版本,但依然不如 MySQL/PG 直观
示例:统计某字段中数组元素总数(假设字段值就是 JSON 数组字符串)
SELECT SUM(cnt) FROM ( SELECT COUNT(*) AS cnt FROM t, json_each(t.json_col) GROUP BY t.rowid );
为什么不用 JSON_LENGTH() 直接统计,而要展开?
因为 JSON_LENGTH()(MySQL)或 jsonb_array_length()(PG)确实能快速拿到数组长度,但它们只解决“单个数组有多少元素”,不解决“数组里每个元素是什么”“按元素值分组统计”“排除重复元素”等需求。一旦你要做 COUNT(DISTINCT elem)、WHERE elem LIKE '%foo%'、或和另一张表 JOIN,就必须展开。
容易被忽略的关键点:展开操作本质是“行扩展”,一条记录变 N 条,不仅影响结果集大小,还会显著放大 JOIN、WHERE 和聚合的计算量。线上大表慎用未加限制的 JSON_TABLE 或 jsonb_array_elements(),尤其当平均数组长度 > 100 时,可能让查询从毫秒级变成秒级甚至超时。










