SQL查询一对多关系中不含指定关联值的主表记录方法
查询不包含指定标签的多对多关联记录SQL实现
表结构与需求说明
- 共三张表:
Exercise存储练习记录,Tag存储标签,TagsExercises为中间关联表,实现练习与标签的多对多关系,单个练习可关联多个标签 - 核心需求:返回完全未绑定指定标签的所有练习记录
示例数据
Exercise表内容: Exercise 1 Exercise 2 Exercise 3 Tag表内容: Tag 1 Tag 2 TagsExercises关联关系: Exercise 1 - Tag 1 Exercise 1 - Tag 2 Exercise 2 - Tag 1 Exercise 3 - Tag 2
验证场景:查询不包含Tag 1的练习,预期返回结果仅为Exercise 3
原有SQL的错误原因
你之前编写的SQL是行级过滤逻辑:只要某条关联记录的标签ID不等于要排除的ID,该行就会被保留。比如Exercise 1同时关联Tag1和Tag2,其中关联Tag2的记录满足Tags.id NOT IN的判断条件,就会被错误返回,最终得到不符合预期的结果。
正确SQL写法
方案1:NOT EXISTS 反向判断(推荐,性能最优)
逻辑:直接筛选不存在「绑定了待排除标签」关联记录的练习
SELECT e.id FROM Exercise e WHERE NOT EXISTS ( SELECT 1 FROM TagsExercises te WHERE te.idExercise = e.id AND te.idTag = '1' -- 此处替换为需要排除的标签ID );
方案2:左连接+空值判断
逻辑:左连接练习与待排除标签的关联关系,筛选关联为空(即未绑定该标签)的练习
SELECT e.id FROM Exercise e LEFT JOIN TagsExercises te ON e.id = te.idExercise AND te.idTag = '1' -- 此处替换为需要排除的标签ID WHERE te.idExercise IS NULL;
方案3:分组聚合判断
逻辑:按练习分组,统计每个练习绑定待排除标签的总次数,筛选次数为0的记录
SELECT e.id FROM Exercise e LEFT JOIN TagsExercises te ON e.id = te.idExercise GROUP BY e.id HAVING SUM(te.idTag = '1') = 0; -- 此处替换为需要排除的标签ID
扩展说明
如果需要同时排除多个标签,仅需调整标签判断条件即可:
- NOT EXISTS写法:将条件改为
te.idTag IN ('1','2')(括号内为所有待排除的标签ID) - 聚合写法:将条件改为
SUM(te.idTag IN ('1','2')) = 0 - 不建议继续使用隐式逗号连接表的写法,统一使用显式JOIN语法可避免大量连接逻辑错误。
内容的提问来源于stack exchange,提问作者Dorian Hawkmoon
相关产品推荐
相关产品推荐

