讲师中心 微信公众号
AI工具推荐 视频效率加速

SQL子查询如何实现多级分类数据筛选

胖涛吖_9458

胖涛吖_9458

发布时间:2026-08-09 14:55:47

|

326人浏览过

|

来源于php中文网

原创

子查询无法处理多级树形遍历,仅支持单层映射;多级统计必须用递归CTE(如WITH RECURSIVE)或固定层数自连接,MySQL 5.7及以下不支持递归需退化处理。

sql子查询如何实现多级分类数据筛选

子查询放WHERE里筛某一级分类,别想一次筛多级

想用子查询直接“一次性查出所有三级分类下的商品”,这条路走不通。子查询本身不处理树形结构遍历,它只解决“已知父ID集合 → 查对应子数据”这种单层映射。比如要查所有一级分类(parent_id = 0)下的商品,可以这样写:

SELECT * FROM product 
WHERE category_id IN (
  SELECT id FROM category WHERE parent_id = 0
);

但如果你把parent_id = 0换成parent_id IN (SELECT id FROM category WHERE parent_id = 0),试图递进到二级,MySQL 5.7 会允许,MySQL 8.0+ 和 PostgreSQL 则可能因无法推导层级而返回空或报错——这不是语法错,是逻辑断层。

  • 子查询在WHERE中只能展开一层间接关系,再深就得靠其他机制
  • 嵌套两层IN(如 A→B→C)在老版本 MySQL 中可能跑通,但结果不可靠:若中间某层无匹配,整条链就断,且无法区分是哪一级缺失
  • 真正需要“从根到叶子全路径”的筛选,必须先拿到完整路径集,而不是靠子查询层层跳

多级统计必须用派生表 + 别名,漏掉AS就报错

要统计每个一级分类下有多少商品、多少二级分类、多少三级分类,得把每层聚合结果分别做成派生表,再用JOIN拼起来。最常踩的坑就是子查询没加别名:

SELECT c.name, p1.cnt AS level1_cnt, p2.cnt AS level2_cnt
FROM category c
LEFT JOIN (
  SELECT parent_id, COUNT(*) AS cnt 
  FROM product 
  GROUP BY parent_id
) AS p1 ON c.id = p1.parent_id
LEFT JOIN (
  SELECT parent_id, COUNT(*) AS cnt 
  FROM category 
  WHERE parent_id != 0 
  GROUP BY parent_id
) AS p2 ON c.id = p2.parent_id;

注意:AS p1和AS p2不能省——MySQL 8.0+/PostgreSQL 会直接报ERROR 1248 (42000): Every derived table must have its own alias;SQL Server 虽容忍空格别名,但为跨平台一致,统一用AS最稳。

  • 派生表里的GROUP BY字段必须和SELECT中非聚合字段完全一致,否则 MySQL 8.0+ 报ERROR 1055
  • 如果category表里用parent_id = 0表示一级,那二级统计就得WHERE parent_id IN (SELECT id FROM category WHERE parent_id = 0),不能硬写parent_id != 0
  • 别在派生表里JOIN大表(如product连order),先聚合出id + count,外层再关联维度表

真要遍历层级,用WITH RECURSIVE,子查询顶不上

当需求明确是“列出所有分类及其深度,并统计各级商品数”,就必须放弃纯子查询思路,改用递归CTE。这是唯一能自然表达树遍历的标准方案:

WITH RECURSIVE tree AS (
  SELECT id, name, parent_id, 1 AS level
  FROM category WHERE parent_id = 0
  UNION ALL
  SELECT c.id, c.name, c.parent_id, t.level + 1
  FROM category c
  INNER JOIN tree t ON c.parent_id = t.id
)
SELECT t.level, t.name, COUNT(p.id) AS product_cnt
FROM tree t
LEFT JOIN product p ON t.id = p.category_id
GROUP BY t.level, t.name;

这条语句在 PostgreSQL、SQL Server、MySQL 8.0+ 上都能跑,但 MySQL 5.7 及更早版本不支持WITH RECURSIVE,只能退回到固定层数自连接——比如最多支持三级,就得写三层LEFT JOIN,代码冗长且难维护。

  • 递归CTE的锚点(第一部分)必须不含循环,否则会无限执行直到超限
  • UNION ALL比UNION快,因为不查重;分类ID天然唯一,无需去重
  • 如果分类表里有环(比如A的parent_id指向B,B又指回A),递归会失败,上线前务必校验数据

NOT EXISTS比NOT IN安全,尤其字段可能为NULL时

筛选“从未发布过文章的分类”,容易写成WHERE id NOT IN (SELECT DISTINCT category_id FROM article)。一旦article.category_id里有NULL,整个NOT IN条件变成UNKNOWN,结果为空——不报错,也不提示,静默丢失数据。

正确写法是用NOT EXISTS:

SELECT id, name FROM category c
WHERE NOT EXISTS (
  SELECT 1 FROM article a WHERE a.category_id = c.id
);

NOT EXISTS不关心子查询返回什么值,只判断是否存在匹配行,天然绕过NULL陷阱。

  • EXISTS和NOT EXISTS都支持相关子查询,性能取决于category_id是否有索引
  • 如果article表极大,而category只有几百行,NOT EXISTS比LEFT JOIN ... IS NULL更直观且通常更快
  • 别在NOT EXISTS子查询里写SELECT *,用SELECT 1即可,减少解析开销

子查询能解决单层关联和静态集合筛选,但树形结构的动态深度、跨层聚合、路径依赖,必须交给递归CTE或应用层预计算。很多人卡在“为什么子查询嵌套三层就出错”,其实不是写法错,是问题本身超出了子查询的能力边界。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

4383

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

911

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

1149

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

6321

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

3143

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

6300

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

8561

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

1170

2024.04.29

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

80

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn