PostgreSQL多条件查询:筛选仅晚到未正常签到的用户数据
解决PostgreSQL中排除已有早时段签到记录的晚到用户查询问题
嘿,我发现你当前的查询返回空结果的核心问题了——你的NOT EXISTS子查询没有和外层查询的用户ID做关联!
原查询里的子查询只是检查整个checkinout表在2017-12-28 06:15:00到2017-12-28 07:45:00之间有没有任何记录,只要存在哪怕一条,NOT EXISTS就会返回false,直接把主查询里所有符合晚时段条件的用户都排除掉了,这显然不是你想要的逻辑。
你的需求是要找出:在晚时段(07:45-13:45)有签到,且同一个用户在早时段(06:15-07:45)完全没有签到记录的人,对吧?那我们需要修正子查询,让它针对每个用户单独检查早时段的签到情况。
修正后的查询(NOT EXISTS版本)
SELECT DISTINCT ON (co.userid) co.userid, ui.name FROM checkinout co JOIN userinfo ui ON ui.userid = co.userid WHERE co.checktime BETWEEN '2017-12-28 07:45:00' AND '2017-12-28 13:45:00' AND NOT EXISTS ( SELECT 1 -- EXISTS只关心存在性,选1比选字段更高效 FROM checkinout co_early WHERE co_early.userid = co.userid -- 关键:关联外层的用户ID,确保检查同一个用户 AND co_early.checktime BETWEEN '2017-12-28 06:15:00' AND '2017-12-28 07:45:00' );
关键修正点
- 给子查询的
checkinout表起了别名co_early,并添加co_early.userid = co.userid的关联条件,这样子查询只会检查当前用户(外层查询的用户)在早时段有没有签到记录 - 子查询用
SELECT 1代替SELECT co.userid,因为EXISTS只判断是否存在匹配记录,不需要返回具体字段,这样性能更优 - 保留
DISTINCT ON (co.userid),确保同一个用户在晚时段有多次签到时,只返回一条记录
另一种实现方式(LEFT JOIN + IS NULL)
如果你习惯用JOIN的写法,也可以用左连接后判断空值的方式实现相同逻辑:
SELECT DISTINCT ON (co.userid) co.userid, ui.name FROM checkinout co JOIN userinfo ui ON ui.userid = co.userid LEFT JOIN checkinout co_early ON co_early.userid = co.userid AND co_early.checktime BETWEEN '2017-12-28 06:15:00' AND '2017-12-28 07:45:00' WHERE co.checktime BETWEEN '2017-12-28 07:45:00' AND '2017-12-28 13:45:00' AND co_early.userid IS NULL; -- 说明该用户没有早时段的签到记录
这两种写法在PostgreSQL中的性能表现基本一致,你可以根据自己的代码习惯选择。
内容的提问来源于stack exchange,提问作者Alvian Cahyadi
相关产品推荐
相关产品推荐

