SQL数据筛选技术问询:如何保留有子项的父行有效记录与无任何子项的父行,移除已有子项父行的空子项记录
解决父子关系数据的筛选问题
我来帮你搞定这个数据筛选的需求,你的核心规则其实很明确:
- 要是某个父行有至少一条非空的child记录,那它对应的空child记录就得删掉
- 要是某个父行压根没有任何非空child记录(比如示例里的C),那它的空child记录得保留
方案一:用窗口函数实现(推荐,逻辑清晰)
这种方式利用窗口函数统计每个父行的非空child数量,筛选逻辑一目了然:
SELECT parent, child FROM ( SELECT parent, child, -- 统计当前父行下非空child的总数(COUNT自动忽略NULL) COUNT(child) OVER (PARTITION BY parent) AS non_null_child_count FROM my_table ) AS sub_query -- 筛选条件:要么child非空,要么该父行没有任何非空child WHERE child IS NOT NULL OR non_null_child_count = 0;
逻辑拆解
- 内层子查询里,
COUNT(child) OVER (PARTITION BY parent)会给每一行标记出它所属父行的非空child总数——因为COUNT对NULL值不计数,所以这个数值刚好能帮我们判断父行有没有有效子记录。 - 外层的筛选条件直接对应你的需求:
- 只要
child不为空,直接保留这条记录 - 要是某个父行的
non_null_child_count是0,说明它没有任何有效子记录,那就保留它的空child记录
- 只要
用你的示例数据验证
输入数据:
| parent | child |
|---|---|
| A | D |
| A | |
| B | E |
| B | F |
| C |
内层子查询会生成这样的中间结果:
| parent | child | non_null_child_count |
|---|---|---|
| A | D | 1 |
| A | 1 | |
| B | E | 2 |
| B | F | 2 |
| C | 0 |
经过外层筛选后,会去掉A对应的空child记录,最终结果完全符合你的期望:
| parent | child |
|---|---|
| A | D |
| B | E |
| B | F |
| C |
方案二:兼容旧版数据库的子查询实现
如果你的数据库不支持窗口函数(比如某些老版本的MySQL),可以用关联子查询来实现:
SELECT t1.parent, t1.child FROM my_table t1 LEFT JOIN ( -- 先找出所有有非空child记录的父行 SELECT parent FROM my_table WHERE child IS NOT NULL GROUP BY parent ) t2 ON t1.parent = t2.parent -- 筛选逻辑:要么当前记录child非空,要么父行不在有非空child的列表里 WHERE t1.child IS NOT NULL OR t2.parent IS NULL;
这个方案的思路是先把所有有有效子记录的父行找出来,然后筛选时排除那些属于这类父行的空child记录,同时保留没有有效子记录的父行的空记录。
内容的提问来源于stack exchange,提问作者Ricardo Francois
相关产品推荐
相关产品推荐

