SQL如何筛选所有客户消费占比均≥70%的场所名称
需求说明
现有存储客户场所消费信息的数据表,需要筛选出符合以下规则的场所名称:所有在该场所产生消费的客户,在该场所的消费额占自身总消费额的比例均不低于70%。
提前计算好单客户单场所消费占比的中间表c示例数据如下:
| client_id | place_name | total_purchase | detail_purchase | percent |
|---|---|---|---|---|
| 1 | place1 | 10 | 7 | 0.7 |
| 1 | place2 | 10 | 3 | 0.3 |
| 2 | place1 | 5 | 4 | 0.8 |
| 2 | place3 | 5 | 1 | 0.2 |
按规则预期输出结果为place1,该场所下所有关联客户的消费占比都满足≥70%的要求。
原有Python实现逻辑是遍历所有场所,判断场所下所有消费记录的percent字段是否都≥0.7,和Python内置all()函数的判断逻辑一致。
原有SQL错误原因
之前编写的SQL存在逻辑问题:HAVING子句中直接写total_purchase/detail_purchase >=0.7既没有做聚合计算,也没有对分组内的所有记录做全量校验,完全没有实现「组内所有记录都满足条件」的判断,因此无法得到正确结果。
可行SQL实现
SQL中实现类似Pythonall()的判断逻辑非常简单:按place_name分组后,统计组内不满足占比≥0.7要求的记录条数,如果这个条数为0,就代表该分组下所有记录都符合要求。
如果已经生成了带percent字段的中间表c,直接执行以下语句即可:
SELECT place_name FROM c GROUP BY place_name HAVING SUM(CASE WHEN percent < 0.7 THEN 1 ELSE 0 END) = 0;
如果需要从最原始的消费明细表(假设原始表为consume_records,存储每一笔客户消费记录,字段包含client_id、place_name、amount)直接计算,不需要提前生成中间表,可以用窗口函数一步完成计算:
WITH client_place_consume AS ( SELECT client_id, place_name, -- 单客户在单场所的消费额 / 单客户总消费额 = 占比 SUM(amount) / SUM(SUM(amount)) OVER (PARTITION BY client_id) AS percent FROM consume_records GROUP BY client_id, place_name ) SELECT place_name FROM client_place_consume GROUP BY place_name HAVING SUM(CASE WHEN percent < 0.7 THEN 1 ELSE 0 END) = 0;
结果校验
代入示例数据验证逻辑:
place1分组下共2条记录,percent分别为0.7、0.8,无小于0.7的记录,统计不满足条件的记录数为0,符合要求被返回place2分组下共1条记录,percent为0.3<0.7,不满足条件的记录数为1,被过滤place3分组下共1条记录,percent为0.2<0.7,不满足条件的记录数为1,被过滤
最终返回结果和预期完全一致。
内容的提问来源于stack exchange,提问作者juan escorcia
相关产品推荐
相关产品推荐

