如何在PostgreSQL中筛选每月均有至少1笔成功支付与1笔失败支付的用户
实现符合要求的用户筛选方案
嘿,这个需求核心是要验证用户的每一个有支付记录的月份,都同时存在成功和失败支付,我们可以通过两层聚合查询来实现,我给你详细拆解:
思路拆解
- 先按「用户+月份」分组,统计每个分组内是否同时存在成功(
success=true)和失败(success=false)的支付; - 再按「用户」分组,检查该用户的所有月份分组是否都满足「同时有成功和失败支付」的条件。
针对PostgreSQL的实现方案
如果你的数据库是PostgreSQL(支持BOOL_OR/BOOL_AND这类布尔聚合函数),可以用下面的高效写法:
SELECT user_id FROM ( -- 第一步:统计每个用户每个月份的支付状态覆盖情况 SELECT user_id, date_trunc('month', paydate) AS pay_month, BOOL_OR(success) AS has_success, -- 该月是否至少有1笔成功支付 BOOL_OR(NOT success) AS has_failed -- 该月是否至少有1笔失败支付 FROM payments GROUP BY user_id, pay_month ) AS user_month_status GROUP BY user_id -- 第二步:验证该用户的所有月份都同时具备成功和失败支付 HAVING BOOL_AND(has_success AND has_failed) = TRUE
代码解释
- 内层子查询
user_month_status:用BOOL_OR快速判断每个用户的每个月份是否存在对应状态的支付(比COUNT更高效,找到第一个符合条件的记录就停止计算); - 外层查询的
HAVING子句:BOOL_AND(has_success AND has_failed)会检查该用户的所有月份是否都同时满足「有成功且有失败」,只有全部满足的用户才会被筛选出来。
通用数据库兼容方案(比如MySQL)
如果你的数据库不支持布尔聚合函数,我们可以用COUNT(DISTINCT success)来判断状态覆盖情况:
SELECT user_id FROM ( SELECT user_id, DATE_FORMAT(paydate, '%Y-%m') AS pay_month, -- MySQL的月份格式化方式,其他数据库可调整 COUNT(DISTINCT success) AS status_type_count FROM payments GROUP BY user_id, pay_month ) AS user_month_status GROUP BY user_id -- 所有月份的状态类型数都必须是2(同时有true和false) HAVING MIN(status_type_count) = 2
代码解释
- 内层子查询统计每个用户每个月份的不同支付状态数量,如果等于2,说明该月同时有成功和失败支付;
- 外层查询通过
MIN(status_type_count) = 2确保该用户的所有月份都满足状态数为2,也就是每个月都同时有两种支付结果。
内容的提问来源于stack exchange,提问作者gurbajani
相关产品推荐
相关产品推荐

