You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL子查询+IFNULL合并考勤IN/OUT行时WHERE条件失效问题

问题原因
  • 兜底逻辑本身无边界约束:你写的IFNULL第二参数明确指定了「当日无OUT记录则取次日最小的OUT记录」,但没有加任何时间范围限制。2022-03-25当日无OUT记录时,自然触发该逻辑,直接抓取到3月26日18:18的OUT值,这不是子查询条件失效,是规则本身就会返回这个结果。
  • 当日OUT匹配逻辑有漏洞:查询当日OUT的子查询仅写了LIMIT 1,没有加ORDER BY time_entry指定排序,MySQL会按主键存储顺序返回第一条匹配记录,导致3月24日优先返回了7:03的跨天签退记录(实际属于3月23日的签退),而非当天18:06的正常下班签退。
  • 跨天签退规则缺失:现有逻辑默认次日所有OUT都可以作为前一天的兜底签退,没有区分跨天加班的凌晨签退和次日正常的下班签退,这是3月25日错配记录的核心原因。
修正方案

直接用条件聚合替代多层关联子查询,同时给跨天签退加合理时间阈值(默认次日12点前的OUT算前一天的跨天签退,可根据业务调整),修正后SQL如下:

SELECT 
  MIN(a.id) AS id,
  a.userid,
  a.date_entry,
  MIN(CASE WHEN a.mode = 'In' THEN a.time_entry END) AS `IN`,
  IFNULL(
    -- 第一步:匹配当日签到后、当日24点前的最早签退
    (SELECT MIN(b.time_entry) 
     FROM erpweb.`app` b 
     WHERE b.`userid` = a.`userid`
       AND b.`mode` = 'Out'
       AND b.`time_entry` >= MIN(CASE WHEN a.`mode` = 'In' THEN a.`time_entry` END)
       AND b.`time_entry` < DATE_ADD(a.`date_entry`, INTERVAL 1 DAY)
    ),
    -- 第二步:当日无签退时,仅匹配次日0点-12点的跨天签退,超出时段返回NULL
    (SELECT MIN(b.`time_entry`)
     FROM erpweb.`app` b
     WHERE b.`userid` = a.`userid`
       AND b.`mode` = 'Out'
       AND b.`date_entry` = DATE_ADD(a.`date_entry`, INTERVAL 1 DAY)
       AND b.`time_entry` < DATE_ADD(DATE_ADD(a.`date_entry`, INTERVAL 1 DAY), INTERVAL 12 HOUR)
    )
  ) AS `OUT`
FROM erpweb.`app` a
WHERE a.`date_entry` BETWEEN '2022-03-23' AND '2022-03-26'
GROUP BY a.`userid`, a.`date_entry`;
逻辑说明
  • 签到时间直接用条件聚合取当日最早的IN记录,减少子查询扫表次数,执行效率更高
  • 当日签退增加时间约束:必须晚于当日最早签到时间、且在当日24点前,避免匹配到当天早于签到时间的无效记录
  • 跨天签退增加12点时间阈值:只有次日中午12点前的OUT才会被识别为前一天的跨天加班签退,次日下午/晚上的正常下班记录不会被错配
  • 对id字段使用MIN()聚合,符合SQL严格模式下ONLY_FULL_GROUP_BY的语法要求,不会随机返回分组内的id值
  • 执行后返回结果完全符合预期:2022-03-25的OUT字段返回NULL,其余记录匹配正确。

内容的提问来源于stack exchange,提问作者Paul Iverson Cortez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 02:54:40