
在 Laravel 中,对 SELECT 语句中通过 DB::raw() 定义的派生字段(如 correctTime)进行非空过滤时,不能使用 whereNotNull(),而必须使用 havingRaw(),因为该字段不存在于原始表中,仅在查询结果集生成后才存在。
在 laravel 中,对 `select` 语句中通过 `db::raw()` 定义的派生字段(如 `correcttime`)进行非空过滤时,不能使用 `wherenotnull()`,而必须使用 `havingraw()`,因为该字段不存在于原始表中,仅在查询结果集生成后才存在。
当你在 Eloquent 查询中使用 DB::raw() 创建计算列(例如 IF(time IS NOT NULL, time, attendance_corrections.correct_time) as correctTime),该列属于 结果集中的别名字段(alias),并非数据库表的真实列。因此:
- where() 和 whereNotNull() 作用于 WHERE 子句,在数据分组和聚合前执行,只能引用原始表字段;
- 而 having()(及其衍生方法 havingRaw())作用于 HAVING 子句,在 GROUP BY 或聚合计算之后执行,可用于过滤包含 SELECT 中别名或聚合函数的结果行。
✅ 正确做法:在链式查询末尾添加 ->havingRaw("correctTime IS NOT NULL"),并确保该查询逻辑上支持 HAVING(通常需配合 GROUP BY,但 Laravel 对无显式 GROUP BY 的 havingRaw 也兼容——底层会自动适配为 WHERE 等效逻辑,或由数据库引擎优化处理)。
以下是修正后的完整代码示例:
$employee_calendar->attendance = Attendance::leftJoin("attendance_corrections", function ($join) {
$join->on("attendance_corrections.attendance_id", "=", "attendance.id")
->where("attendance_corrections.status", "!=", "rejected");
})
->select('attendance.*', DB::raw('IF(`time` IS NOT NULL, `time`, attendance_corrections.correct_time) as `correctTime`'))
->where("attendance.employee_id", $employee->id)
->whereDate("attendance.date", "=", $employee_calendar->date)
->orderBy('correctTime', 'asc')
->with("requests")
->distinct()
->havingRaw("correctTime IS NOT NULL") // ✅ 关键修复:用 havingRaw 替代 whereNotNull
->get();⚠️ 注意事项:
- 若同时使用 groupBy(),havingRaw() 是标准且必需的方式;
- 即使未显式调用 groupBy(),Laravel 仍允许 havingRaw(),但需确保数据库驱动(如 MySQL)支持该语法;
- 避免在 havingRaw() 中直接拼接用户输入,防止 SQL 注入;如有动态值,应使用参数绑定:->havingRaw("correctTime IS NOT NULL AND status = ?", [$status]);
- distinct() 与 havingRaw() 共存时逻辑清晰,但若性能敏感,建议结合索引优化 attendance.date 和 attendance.employee_id 字段。
总结:派生字段过滤 ≠ 原始字段过滤。牢记「where 查源表,having 查结果」这一原则,即可准确、安全地完成类似 correctTime IS NOT NULL 的业务筛选需求。


















