统计每年仅含取消会话的学员:哪条SQL查询正确?
统计每年仅拥有取消状态会话的学员数量:SQL查询对比分析
我有一个名为sessions的表,表结构及部分数据如下:
| session_id | session_date_time | mentor_id | mentee_id | session_status | mentor_domain_id | |
|---|---|---|---|---|---|---|
| 0 | 1 | 2021-02-12 00:00:00 | 30009 | 20136 | canceled | 3 |
| 1 | 2 | 2021-02-12 00:00:00 | 30032 | 20003 | finished | 6 |
| 2 | 3 | 2021-02-12 00:00:00 | 30020 | 20026 | finished | 2 |
| 3 | 4 | 2021-02-12 00:00:00 | 30025 | 20006 | finished | 6 |
| 4 | 5 | 2021-02-12 00:00:00 | 30011 | 20006 | finished | 5 |
| 5 | 6 | 2021-02-13 00:00:00 | 30036 | 20056 | finished | 5 |
| 6 | 7 | 2021-02-13 00:00:00 | 30015 | 20130 | canceled | 6 |
| 7 | 8 | 2021-02-13 00:00:00 | 30009 | 20073 | finished | 7 |
| 8 | 9 | 2021-02-13 00:00:00 | 30010 | 20033 | finished | 5 |
| 9 | 10 | 2021-02-13 00:00:00 | 30004 | 20047 | finished | 5 |
| 10 | 11 | 2021-02-14 00:00:00 | 30004 | 20153 | finished | 6 |
| 11 | 12 | 2021-02-15 00:00:00 | 30015 | 20089 | canceled | 7 |
| 12 | 13 | 2021-02-17 00:00:00 | 30046 | 20162 | finished | 6 |
| 13 | 14 | 2021-02-17 00:00:00 | 30001 | 20012 | finished | 7 |
| 14 | 15 | 2021-02-17 00:00:00 | 30025 | 20126 | finished | 3 |
| 15 | 16 | 2021-02-17 00:00:00 | 30009 | 20118 | canceled | 7 |
| 16 | 17 | 2021-02-17 00:00:00 | 30017 | 20098 | finished | 7 |
| 17 | 18 | 2021-02-18 00:00:00 | 30025 | 20112 | canceled | 6 |
| 18 | 19 | 2021-02-18 00:00:00 | 30004 | 20072 | finished | 5 |
| 19 | 20 | 2021-02-18 00:00:00 | 30004 | 20110 | finished | 5 |
| 20 | 21 | 2021-02-19 00:00:00 | 30015 | 20160 | canceled | 4 |
| 21 | 22 | 2021-02-20 00:00:00 | 30044 | 20164 | finished | 4 |
| 22 | 23 | 2021-02-20 00:00:00 | 30002 | 20031 | finished | 2 |
| 10417 | 10418 | 2022-09-15 00:00:00 | 30384 | 21802 | finished | 3 |
| 10418 | 10419 | 2022-09-15 00:00:00 | 30318 | 21837 | finished | 4 |
| 10419 | 10420 | 2022-09-15 00:00:00 | 30424 | 21842 | finished | 5 |
| 10420 | 10421 | 2022-09-15 00:00:00 | 30623 | 21591 | canceled | 6 |
我需要统计每年仅拥有取消状态会话的学员数量,先后编写了两条SQL查询:
第一条SQL查询及结果
select date_trunc('year', session_date_time) as year_dt, count(distinct mentee_id) as mentee_can_ses, round((count(distinct mentee_id)-lag(count(distinct mentee_id)) over())*1.0/lag(count(distinct mentee_id)) over(), 2) as mentee_dynamic from sessions as s where mentee_id not in (select distinct mentee_id from sessions as s where session_status = 'finished') group by date_trunc('year', session_date_time)
查询结果:2021年11名学员,2022年20名学员。
第二条SQL查询及结果
with table1 as ( select mentee_id, date_trunc('year', session_date_time) as year_dt, count(session_id) filter(where session_status='finished') as ses_finished, count(session_id) filter(where session_status='canceled') as ses_canceled from sessions as s group by mentee_id, date_trunc('year', session_date_time) having count(session_id) filter(where session_status='finished') = 0 ) select year_dt, count(distinct mentee_id) from table1 group by year_dt
(注:原查询中select distinct mentee_id多余,因为已经按mentee_id分组,每个分组对应唯一的mentee_id)
查询结果:2021年102名仅含取消会话的学员,2022年37名。
哪条查询正确?错误原因是什么?
第二条查询是正确的,第一条查询存在逻辑错误:
第一条查询的核心错误:
子查询select distinct mentee_id from sessions where session_status = 'finished'获取的是所有历史上有过完成会话的学员ID,不管这些完成会话发生在哪一年。外层的where mentee_id not in (...)会把这些学员全部过滤掉——哪怕某个学员在2021年只有取消会话,但2022年有完成会话,这个学员也会被排除在2021年的统计结果之外,完全不符合“按年度统计仅取消会话学员”的需求。第二条查询的逻辑合理性:
- 先按
mentee_id和年度分组,精准统计每个学员在每一年的完成会话数和取消会话数; - 通过
having子句筛选出当年没有任何完成会话的学员(即该学员在当年的所有会话都是取消状态); - 最后按年度汇总这些学员的数量,完全匹配需求。
- 先按
另外,第二条查询中的select distinct mentee_id是多余的,因为已经按mentee_id分组,每个分组只会返回一个唯一的mentee_id,可以直接去掉distinct。
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

