发布于2026-07-20 阅读(0)
扫一扫,手机访问
本文讲解如何在 Lara vel 中正确构建查询条件,使记录满足“大于等于开始时间、且仅当结束时间存在时小于等于结束时间”的逻辑,避免因 end_at 为 NULL 导致查询失效。
聊一个业务里经常碰到的“小坑”——时间范围筛选,但结束日期偏偏是可空的。比如活动有效期、价格策略、权限有效期这类场景,通常会有个必填的start_at,和一个可选的end_at。如果end_at为空,表示长期有效;不为空,那就得严格落在 [start_at, end_at] 这个闭区间内。听起来简单,写查询的时候却容易翻车。
直接上 whereBetween()?不行,因为那个方法默认两端都得非空,给个null进去,SQL 直接报错或者出假结果。写两个where条件固定住?更糟——where('end_at', '>=', $effectiveDate) 碰上 end_at 是 NULL,SQL 里 NULL 跟任何值比较都返回 UNKNOWN,整行记录就被过滤掉了,那些本该长期有效的记录全没了。
正确做法是条件化查询,用闭包把“或”逻辑包装进去。看这段代码:
$effectiveDate = '2022-04-06 23:34:00';
GroupExtras::query()
->select('group_extras.*')
->join('groups', 'group_extras.group_id', '=', 'groups.id')
->where('group_extras.start_at', '<=', $effectiveDate)
->where(function ($query) use ($effectiveDate) {
// 分支一:end_at 为 NULL → 视为无限期,无需额外限制
$query->whereNull('group_extras.end_at')
// 分支二:end_at 非 NULL → 必须 >= effectiveDate
->orWhere(function ($sub) use ($effectiveDate) {
$sub->whereNotNull('group_extras.end_at')
->where('group_extras.end_at', '>=', $effectiveDate);
});
})
->orderBy('group_extras.value')
->first();
关键逻辑拆开看:
start_at <= $effectiveDate 先把所有已生效的记录筛出来。whereNull() 匹配无结束日期的行,再通过 orWhere() 嵌套一个子条件:只有那些有结束日期且还没过期的才保留。这样两个分支合并,正好覆盖“长期有效”和“有限期”两种场景。where() 里直接写 end_at >= ? 然后传 NULL,SQL 里 NULL 比较返回 UNKNOWN,行会被排除,这是最隐蔽的坑。另外有几个小提醒:
whereBetween('column', [$start, $end]),它底层生成 BETWEEN $start AND $end,$end 是 null 时直接非法。Y-m-d H:i:s 字符串,或者显式用 DB::raw() 处理,避免隐式转换出问题。start_at 和 end_at 上建联合索引,比如 INDEX(start_at, end_at),这个查询的效率会好很多。这个方案写起来语义清晰,也兼容主流数据库,算是 Lara vel 里处理“半开放时间区间”的标准思路了。下次遇到类似需求,直接套用这个闭包模板就行,不用再纠结 NULL 导致的“幽灵过滤”。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8