2017-04-04 17 views
2

Existe-t-il un moyen d'optimiser la requête suivante?Test efficace MySQL si le compte w/where est supérieur à une valeur

SELECT count(*)>1000 FROM table_with_lot_of_rows WHERE condition_on_index; 

En utilisant cette requête, MySQL effectue d'abord le count(*) puis la comparaison. C'est rapide quand seulement quelques lignes satisfont la condition, mais peuvent prendre une éternité si beaucoup de lignes le satisfont. Y at-il un moyen d'arrêter de compter dès que 1000 articles sont trouvés, au lieu de passer par tous les résultats?

En particulier, je suis intéressé par la table MyISAM avec la condition de texte intégral, mais toute réponse pour InnoDB et/ou la clause WHERE de base aidera.

Répondre

4
SELECT 1 
    FROM table_with_lot_of_rows 
    WHERE condition_on_index 
    LIMIT 1000, 1; 

fonctionne de cette façon:

  1. Utilisation de l'index (qui est probablement plus rapide que d'utiliser les données)
  2. Passer plus de 1000 lignes, la collecte rien. (Cela vaut mieux que d'autres réponses.)
  3. Si vous arrivez si loin, récupérez 1 ligne, contenant uniquement le littéral 1 (dans le SELECT).
  4. Maintenant, il faut soit

un jeu de résultats vide (< = 1000 lignes) ou une rangée de 1 (au moins 1001 lignes). Puis, en fonction de la langue de votre application, il est facile de distinguer les deux cas.

Une autre note: Si ce doit être une sous-requête dans une plus grande requête, puis faire

EXISTS (SELECT 1 
    FROM table_with_lot_of_rows 
    WHERE condition_on_index 
    LIMIT 1000, 1) 

qui retourne VRAI/FAUX (qui sont synonymes de 1 ou 0). Face à cela, le balayage de 1001 lignes, même de l'index, prendra un certain temps. Je pense que ma formulation est la plus rapide possible.

Autres choses à vérifier: Est-ce InnoDB? Est-ce que EXPLAIN dit "Using index"? Combien de RAM? Quel est le réglage de innodb_buffer_pool_size? Notez que InnoDB a maintenant FULLTEXT, il n'y a donc aucune raison de rester avec MyISAM.

Si vous utilisez MyISAM et que le WHERE est MATCH..., la plupart de mes remarques risquent de ne pas s'appliquer. FULLTEXTprobablement récupère tous les résultats avant de donner le reste du moteur à la chance de faire ces jeux avec ORDER BY et LIMIT.

Veuillez nous montrer la requête réelle, ses EXPLAIN et SHOW CREATE TABLE. Et quel est le véritable objectif? Pour voir si une requête produira "trop" de résultats?

Amélioration possible (selon le contexte)

Depuis mes SELECT initiales rendements scalaire 1 ou NULL, il peut être utilisé dans un contexte booléen tel que WHERE. 1 est TRUE, NULL sera traité comme FALSE. Par conséquent EXISTS est probablement redondant.

De même, 1/NULL peut être transformé en 1/0 ainsi. Note: les parens supplémentaires sont requis.

IFNULL((SELECT ... LIMIT 1000,1), 0) 
+0

Excellente solution, merci! Donner les détails de mon paramètre spécifique pourrait être hors sujet pour cette question, qui était pour l'amélioration sur le 'count (*)> 1000' de base, et non lié aux index. – M1L0U

+0

'FULLTEXT',' SPATIAL', 'PRIMARY', et d'autres index fonctionnent de 4 manières différentes. –

+0

Après avoir offert la prime, je suis arrivé avec une version modifiée de ce qui obtient mon désiré "' 0' ou '1'" au lieu de "rien ou' 1' "comme résultat, sans répéter la valeur de seuil (qui viole DRY), mais c'est toujours basé sur la sous-requête (ce qui est moche). Je vais laisser la prime pour voir si quelqu'un peut le faire sans sous-requête pour les points de style, mais sinon, la prime est à vous pour m'inspirer. Ma version modifiée est juste l'emballage à 'COUNT' les résultats de votre requête:' SELECT COUNT (*) DE (SELECT 1 FROM table_with_lot_of_rows WHERE condition_on_index LIMIT 1000, 1); ' – ShadowRanger

1

Vous pouvez optimiser la requête en utilisant une sous-requête avec une limite:

SELECT count(*)>1000 FROM (
    SELECT 0 table_with_lot_of_rows 
    WHERE condition_on_index 
    LIMIT 1001 
) as truncated_count; 

Dans ce cas, MySQL arrête dès que les lignes assez satisfont à la condition.

+0

Oui, cela devrait fonctionner – MohanaPriyan