2017-10-17 2 views
4

Je souhaite appliquer une condition where à la relation. Voici ce que je fais:Mauvaise performance whereHas dans Laravel

Replay::whereHas('players', function ($query) { 
    $query->where('battletag_name', 'test'); 
})->limit(100); 

Il génère la requête suivante:

select * from `replays` 
where exists (
    select * from `players` 
    where `replays`.`id` = `players`.`replay_id` 
     and `battletag_name` = 'test') 
order by `id` asc 
limit 100; 

qui exécute en 70 secondes. Si je réécris manuellement la requête comme ceci:

select * from `replays` 
where id in (
    select replay_id from `players` 
    where `battletag_name` = 'test') 
order by `id` asc 
limit 100; 

Il s'exécute en 0.4 secondes. Pourquoi where exists est le comportement par défaut si c'est si lent? Existe-t-il un moyen de générer la bonne requête where in avec le constructeur de requête ou ai-je besoin d'injecter du SQL brut? Peut-être que je fais quelque chose de mal tout à fait?

replays table a 4M lignes, players a 40M lignes, toutes les colonnes pertinentes sont indexées, le jeu de données ne rentre pas dans la mémoire du serveur MySQL.

Mise à jour: a constaté que la requête correcte peut être générée comme:

Replay::whereIn('id', function ($query) { 
    $query->select('replay_id')->from('players')->where('battletag_name', 'test'); 
})->limit(100); 

ont encore une question pourquoi exists exécute si mal et pourquoi il est le comportement par défaut

+0

Je suggère Do not SELECT * .. Essayez de SELECT attribut spécifique au lieu de tous. – parkway

+0

Je dois tous les sélectionner pour mon cas. Et même en sélectionnant uniquement la colonne 'id' améliore les performances des requêtes de moins de 1%, il est donc négligeable. – Poma

Répondre

1

Je pense que la performance ne Dépendez de whereHas seulement cela dépend du nombre d'enregistrements que vous avez sélectionné

Plus essayez d'optimiser votre serveur mysql

https://dev.mysql.com/doc/refman/5.7/en/optimize-overview.html

et aussi optimiser votre serveur php

et si vous avez plus rapide demande pourquoi ne pas utiliser l'objet de requête brut de larves

$replay = DB::select('select * from replays where id in (
select replay_id from players where battletag_name = ?) 
order by id asc limit 100', ['test'] 
); 
+0

les deux requêtes sélectionnent exactement 100 lignes à cause de la clause' limit'. whereHas le fait en 70 secondes et whereIn en 0.4 secondes. Les optimisations ne sont pas pertinentes pour la question, car elles réduisent le temps d'exécution des requêtes. – Poma

+0

alors peut-être vous pouvez utiliser la requête brute mentionnée ci-dessus –

+0

requête dans l'application réelle est beaucoup plus complexe que cela avec beaucoup de conditions et j'ai vraiment besoin de constructeur de requête pour cela. Le construire à partir de beaucoup de parties de cordes brutes transformera mon code en spaghetti. – Poma