Mysql 如何在查询中最佳地使用orWhere()?
我的问题是:Mysql 如何在查询中最佳地使用orWhere()?,mysql,laravel,Mysql,Laravel,我的问题是: $notifications = \App\Notification::query()->where('users_scope', 'customer') ->with('userSeen') ->where(function ($query) use ($user) { $query->where('user_id_to', 20288) ->orWhere(function($q) use ($user) {
$notifications = \App\Notification::query()->where('users_scope', 'customer')
->with('userSeen')
->where(function ($query) use ($user) {
$query->where('user_id_to', 20288)
->orWhere(function($q) use ($user) {
$q->Where('user_id_to', null)
->Where(function($q) use ($user) {
$q->where('expire_at', null)->where('created_at', '>=', "2020-04-03 04:18:42");
$q->orWhere('expire_at', '!=', null)->where('expire_at', '>', Carbon::now());
});
});
})
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])->orderBy('id', 'desc')->limit(20)->get();
这里还有关系(用作with()
):
下面是一个场景:上面的查询获取最近20个用户的通知(最新通知列表)。最近,通知
表的大小增加过多。目前它有800多万行。而查询的执行时间超过10秒
注意,在这两个表(notifications
和userSeen
表)上也创建了所有需要的索引。此外,该关系(userSeen
)类似于一个透视表,指示用户是否已看到通知
你知道我该如何重写该查询以使其更优化吗
关于逻辑的解释:
是硬编码的,实际上将是20288
$user->id
- 当
为user\u id\u to
时,表示它是批量通知(必须对所有用户可见)null
- 如果用户在处创建的
处创建的值大于用户在
值,则用户可以看到批量通知
- 有时,批量通知有一个过期时间(如营销活动),如果仍未过期,则必须向用户显示该时间
$notifications = \App\Notification::query()->where('users_scope', 'customer')
->with('userSeen')
->where(function ($query) use ($user) {
$query->where('user_id_to', 20288);
if($user){
$query->orWhere(function($q) use ($user) {
$q->Where('user_id_to', null)
->Where(function($q) use ($user) {
$q->where('expire_at', null)->where('created_at', '>=', "2020-04-03 04:18:42");
$q->orWhere('expire_at', '!=', null)->where('expire_at', '>', Carbon::now());
});
});
}
})
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])->orderBy('id', 'desc')->limit(20)->get();
甚至可以使用when子句
参考:根据您在问题中提到的逻辑,这应该可以满足您的需要,但是,它可能对速度没有多大帮助:
$notifications = \App\Notification::query()
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])
->with('userSeen')
->where('users_scope', 'customer')
->where(function ($query) use ($user) {
$query
->where('user_id_to', $user->id)
->orWhere(function ($query) use ($user) {
$query
->whereNull('user_id_to')
->where('created_at', '>=', $user->created_at)
->where(function ($query) {
$query->whereNull('expire_at')->orWhere('expire_at', '>=', now());
});
});
})
->orderByDesc('id')
->limit(20)
->get();
我还建议尝试两个单独的查询,而不是使用
SQL\u CALC\u FOUND\u ROWS
。以下是几篇解释原因的SO帖子:
然后,您的查询将类似于:
$query = \App\Notification::query()
->with('userSeen')
->where('users_scope', 'customer')
->where(function ($query) use ($user) {
$query
->where('user_id_to', $user->id)
->orWhere(function ($query) use ($user) {
$query
->whereNull('user_id_to')
->where('created_at', '>=', $user->created_at)
->where(function ($query) {
$query->whereNull('expire_at')->orWhere('expire_at', '>=', now());
});
});
})
->orderByDesc('id');
$count = $query->count();
$notifications = $query->limit(20)->get();
或者,您可以使用这样的内容。请您对
$user
逻辑的周围添加一点解释,即20288
是硬编码还是应该是$user->id
,用户位的空值是如何工作的,在
创建的的哪些位在
/到期时要分组?哦,你说得对@Rwd。。我将补充一些关于逻辑的解释。。
$query = \App\Notification::query()
->with('userSeen')
->where('users_scope', 'customer')
->where(function ($query) use ($user) {
$query
->where('user_id_to', $user->id)
->orWhere(function ($query) use ($user) {
$query
->whereNull('user_id_to')
->where('created_at', '>=', $user->created_at)
->where(function ($query) {
$query->whereNull('expire_at')->orWhere('expire_at', '>=', now());
});
});
})
->orderByDesc('id');
$count = $query->count();
$notifications = $query->limit(20)->get();