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

Laravel查询:主表字段为null时取关联表字段排序

Eloquent 考勤记录自定义排序实现

核心问题原因

MySQL 默认ASC升序排序时,NULL值会被判定为最小值,因此time字段为空的记录会排在结果集最顶部,不符合业务排序预期。

实现方案

提供两种可落地的实现方式,按需选择即可:

方案1:数据库层排序(性能优先,推荐数据量较大场景使用)

通过左连取每个考勤记录对应的最新有效修正数据,用orderByRaw写自定义排序规则,排序逻辑在SQL执行阶段完成,性能更高。
替换原有循环内的查询代码如下:

foreach ($employee_calendars as $key => $employee_calendar) {
    $employee_calendar->attendance = Attendance::where("employee_id", $employee->id)
        ->whereDate("date", "=", $employee_calendar->date)
        // 左连子查询:取每个考勤ID对应的最新一条修正记录,可按需加修正状态筛选条件
        ->leftJoinSub(
            AttendanceCorrection::select('attendance_id', 'correct_time')
                ->whereIn('id', function ($query) {
                    $query->selectRaw('MAX(id)')
                        ->from('attendance_correction')
                        ->groupBy('attendance_id');
                    // 如果有修正审核状态字段,可在这里加筛选,例:->where('audit_status', 1)
                }),
            'latest_correction',
            'latest_correction.attendance_id',
            '=',
            'attendance.id'
        )
        // 自定义排序规则
        ->orderByRaw("CASE
            WHEN attendance.time IS NOT NULL THEN attendance.time
            WHEN latest_correction.correct_time IS NOT NULL THEN latest_correction.correct_time
            ELSE '9999-12-31 23:59:59'
        END ASC")
        ->with("requests")
        // 指定查询字段,避免join后修正表同名字段覆盖考勤表原字段
        ->select('attendance.*')
        ->get();
}

方案2:集合层排序(写法简洁,推荐小数据量场景使用)

先按原逻辑取数,拿到Eloquent集合后通过sortBy方法自定义排序逻辑,不需要写原生SQL,代码可读性更高。
替换原有循环内的查询代码如下:

foreach ($employee_calendars as $key => $employee_calendar) {
    $employee_calendar->attendance = Attendance::where("employee_id", $employee->id)
        ->whereDate("date", "=", $employee_calendar->date)
        ->with("requests")
        ->get()
        ->sortBy(function ($attendanceItem) {
            // 原始打卡时间存在,直接用原始时间排序
            if (!empty($attendanceItem->time)) {
                return $attendanceItem->time;
            }
            // 原始时间为空,取最新有效修正记录的correct_time排序
            $validCorrection = $attendanceItem->requests->sortByDesc('id')->first();
            if ($validCorrection && !empty($validCorrection->correct_time)) {
                return $validCorrection->correct_time;
            }
            // 无原始时间也无有效修正时间的异常记录,排到列表最后
            return '9999-12-31 23:59:59';
        })
        ->values(); // 重置集合索引,避免前端遍历出现非连续键名问题
}

注意事项

  • 两个方案默认都取每个考勤记录最新的一条修正数据参与排序,如果业务上修正记录需要审核通过才生效,在对应位置加状态筛选条件即可
  • 无有效时间的异常记录统一用极大时间值占位,会自动排到当日考勤列表末尾,不会出现在顶部
  • 如果使用方案1,必须加select('attendance.*'),避免左连时修正表的id、created_at等同名字段覆盖考勤表原字段值

内容的提问来源于stack exchange,提问作者Taha Malik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 02:01:25