関係にwhere
条件を適用したいと思います。これが私がすることです:
Replay::whereHas('players', function ($query) {
$query->where('battletag_name', 'test');
})->limit(100);
次のクエリが生成されます。
select * from `replays`
where exists (
select * from `players`
where `replays`.`id` = `players`.`replay_id`
and `battletag_name` = 'test')
order by `id` asc
limit 100;
これは70秒で実行されます。このように手動でクエリを書き直すと、次のようになります。
select * from `replays`
where id in (
select replay_id from `players`
where `battletag_name` = 'test')
order by `id` asc
limit 100;
0.4秒で実行されます。非常に遅い場合、なぜwhere exists
がデフォルトの動作であるのですか?クエリビルダーを使用して正しいwhere in
クエリを生成する方法はありますか、それとも生のSQLを挿入する必要がありますか?多分私は完全に何か間違ったことをしているのですか?
replays
テーブルには400万行、players
には4000万行、関連するすべての列にインデックスが付けられ、データセットがMySQLサーバーのメモリに収まりません。
更新:正しいクエリを次のように生成できることがわかりました:
Replay::whereIn('id', function ($query) {
$query->select('replay_id')->from('players')->where('battletag_name', 'test');
})->limit(100);
exists
のパフォーマンスが非常に悪い理由と、それがデフォルトの動作である理由については、まだ疑問があります。
これは、laravelではなくmysqlに関連しています。 joinsとsubqueriesの両方のオプションを使用して、上記と同じことを実行できます。 サブクエリは、通常、結合よりもはるかに低速です。
サブクエリは次のとおりです。
上記の事実が、雄弁なORMがsuquriesを使用している理由です。 しかし、速度は遅くなります!特にデータベースに多くの行がある場合。
クエリの参加バージョンは次のようなものです:
select * from `replays`
join `players` on `replays`.`id` = `players`.`replay_id`
and `battletag_name` = 'test'
order by `id` asc
limit 100;
しかし、今度はselectを変更してgroup byを追加し、他の多くのことに注意する必要がありますが、なぜこれがその答えを超えているのですか?新しいクエリは次のようになります:
select replays.* from `replays`
join `players` on `replays`.`id` = `players`.`replay_id`
and `battletag_name` = 'test'
order by `id` asc
group by replays.id
limit 100;
それが、より複雑に参加する理由です。
Laravelで生のクエリを書くことはできますが、結合クエリの雄弁なサポートは十分にサポートされていません。また、それを支援するパッケージはあまりありません。これは、たとえば次のとおりです。 https://github.com/ fico7489/laravel-eloquent-join
左結合を使用できます
$replies = Replay::orderBy('replays.id')
->leftJoin('players', function ($join) {
$join->on('replays.id', '=', 'players.replay_id');
})
->take(100)
->get();
パフォーマンスはどこに依存しないと思います選択したレコードの数にのみ依存します
さらに、mysqlサーバーを最適化してみてください
https://dev.mysql.com/doc/refman/5.7/en/optimize-overview.html
また、PHPサーバーを最適化します
クエリが高速な場合は、幼虫の生のクエリオブジェクトを使用してみませんか
$replay = DB::select('select * from replays where id in (
select replay_id from players where battletag_name = ?)
order by id asc limit 100', ['test']
);