MySQL: le moyen le plus rapide de compter le nombre de lignes

117

Quelle façon de compter un nombre de lignes devrait être plus rapide dans MySQL?

Ce:

SELECT COUNT(*) FROM ... WHERE ...

Ou, l'alternative:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

On pourrait penser que la première méthode devrait être plus rapide, car il s'agit clairement d'un territoire de base de données et le moteur de base de données devrait être plus rapide que quiconque lors de la détermination de ce genre de choses en interne.

Franz
la source
1
Oh, j'ai trouvé une question similaire ( stackoverflow.com/questions/1855226/… ). Mais alors, j'utilise SELECT 1et non SELECT *. Y a-t-il une différence?
Franz
je ne sais pas, mais il est concevable que ces deux réponses soient identiques - l'optimiseur de requête mysql peut faire la même chose sur chacune. cela dit, le premier est moins ambigu que le second. pourquoi n'écrivez-vous pas des benchmarks et ne le testez-vous pas?
Jesse Cohen
Hum, supposons que j'essaie d'améliorer la visibilité du moteur de recherche de SO en posant une question similaire dans des mots différents;)
Franz
1
La différence est la quantité de données envoyées du côté PHP. Plus vous avez de colonnes, plus SELECT * est lent par rapport à SELECT 1, car toutes les colonnes sont récupérées au lieu du numéro 1. Lorsque vous exécutez mysql_query(), par exemple, l'ensemble des résultats est envoyé à PHP depuis MySQL, indépendamment de ce que vous faire avec ces données.
toon81
Poser une question comme celle-ci est un excellent moyen d'obtenir un aperçu ou de nouvelles idées, mais en fin de compte, si vous avez réellement un scénario spécifique où vous voulez plus de vitesse, vous devrez exécuter des tests pour voir ce qui est le plus rapide.
still_dreaming_1

Réponses:

125

Lorsque vous prenez COUNT(*)en compte les index de colonne, ce sera le meilleur résultat. Mysql avec le moteur MyISAM stocke en fait le nombre de lignes, il ne compte pas toutes les lignes à chaque fois que vous essayez de compter toutes les lignes. (basé sur la colonne de la clé primaire)

Utiliser PHP pour compter les lignes n'est pas très intelligent, car vous devez envoyer des données de mysql vers php. Pourquoi le faire alors que vous pouvez réaliser la même chose du côté mysql?

Si le COUNT(*)est lent, vous devez exécuter EXPLAINla requête et vérifier si les index sont réellement utilisés et où les ajouter.


Ce qui suit n'est pas le moyen le plus rapide , mais il y a un cas où COUNT(*)cela ne convient pas vraiment - lorsque vous commencez à regrouper les résultats, vous pouvez rencontrer un problème, où COUNTne compte pas vraiment toutes les lignes.

La solution est SQL_CALC_FOUND_ROWS. Ceci est généralement utilisé lorsque vous sélectionnez des lignes mais que vous avez toujours besoin de connaître le nombre total de lignes (par exemple, pour la pagination). Lorsque vous sélectionnez des lignes de données, ajoutez simplement le SQL_CALC_FOUND_ROWSmot - clé après SELECT:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

Une fois que vous avez sélectionné les lignes nécessaires, vous pouvez obtenir le nombre avec cette seule requête:

SELECT FOUND_ROWS();

FOUND_ROWS() doit être appelé immédiatement après la requête de sélection des données.


En conclusion, tout se résume en fait au nombre d'entrées que vous avez et au contenu de l'instruction WHERE. Vous devez vraiment faire attention à la façon dont les index sont utilisés, lorsqu'il y a beaucoup de lignes (des dizaines de milliers, des millions et plus).

Mārtiņš Briedis
la source
14
Correction: MyISAMenregistre le nombre de lignes. D'autres moteurs de stockage comme InnoDB ne stockent pas le nombre de lignes et compteront toutes les lignes à chaque fois .
The Scrum Meister
1
Savez-vous lequel sera le plus rapide lorsque vous souhaitez simplement savoir s'il y a une ligne: SELECT 1 FROM ... LIMIT 1ou SELECT COUNT(*) FROM ...?
Franz
1
Il est probablement utile de noter que si vous avez de toute façon besoin des données et que vous voulez seulement un décompte pour la pagination / etc. il est plus efficace d'obtenir les données que de compter les lignes dans votre programme.
Tyzoid
6
Le fait que le moteur stocke le nombre de lignes n'a pas d'importance. La question indique clairement qu'il y a un WHEREarticle.
Álvaro González
1
@Franz SELECT COUNT(*) FROM ...peut prendre un temps considérable, en fonction de ce qui doit être analysé (par exemple, une très grande table ou un index de millions / milliards / billion de lignes). SELECT 1 FROM ... LIMIT 1retourne immédiatement car vous le limitez à la première ligne.
jbo5112
59

Après avoir discuté avec mes coéquipiers, Ricardo nous a dit que le moyen le plus rapide était:

show table status like '<TABLE NAME>' \G

Mais vous devez vous rappeler que le résultat peut ne pas être exact.

Vous pouvez également l'utiliser à partir de la ligne de commande:

$ mysqlshow --status <DATABASE> <TABLE NAME>

Plus d'informations: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

Et vous pouvez trouver une discussion complète sur mysqlperformanceblog

MagMax
la source
2
Pour InnoDB, il s'agit d'une approximation.
Martin Tournoij
2
C'est génial à savoir lorsque vous avez besoin d'une idée approximative du nombre de lignes dans de très grandes tables où count (*) peut littéralement prendre des heures!
Mark Hansen
Cela m'a évité de m'arracher tous mes cheveux. COUNT (*) prenait des années pour compter les 33 millions de lignes et plus de ma base de données. Quoi qu'il en soit, je voulais seulement savoir si ma fonction de suppression de lignes parallélisées fonctionnait ou non. Je n'avais pas besoin d'un nombre exact.
joemar.ct
1
+1 En utilisant le statut du tableau à la place, "COUNT (*)" devrait être la bonne réponse à cette question, de même que "le plus rapide" et non "la précision".
lepe
2
L'utilisation SHOW TABLE STATUS(ou l'équivalent SELECTdans information_schema) est rapide, mais ne gère pas de WHEREclause. Il est précis pour MyISAM, mais imprécis (parfois décalé d'un facteur 2) pour InnoDB.
Rick James
29

Excellente question, bonnes réponses. Voici un moyen rapide de faire écho aux résultats si quelqu'un lit cette page et manque cette partie:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");
Dan Horvat
la source
5
mysql_query est une fonction obsolète depuis PHP 5.5.0.
Omar Tariq
8
Pourquoi pas as count? idest déroutant au premier regard.
Orkhan Alikhanov
Ne répond pas à la question
mentalic
17

Cette requête (qui est similaire à ce que bayuah a posté ) montre un joli résumé de tous les comptes de tables dans une base de données: (version simplifiée de la procédure stockée par Ivan Cachicatari que je recommande vivement).

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Exemple:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |
lépe
la source
Cela me donne un résultat. Mais les résultats de count (1) et celui-ci sont différents. De cette façon, donne toujours un nombre inférieur à la requête count. Des pensées?
Ayyappan Sekar
3
Juste un mot aux lecteurs. Cette méthode est extrêmement rapide mais elle n'est applicable que lorsque vous pouvez travailler avec un nombre approximatif de lignes puisque la valeur stockée dans information_scheman'est pas la même que celle renvoyée par SELECT count(*) FROMau cas où InnoDB est utilisé. Si vous avez besoin d'une valeur stricte, gardez à l'esprit que cette méthode donne une valeur stricte uniquement avec les tables MyISAM. Avec InnoDB, le nombre de lignes est une approximation approximative.
Bartosz Firyn
13

J'ai toujours compris que ce qui suit me donnera les temps de réponse les plus rapides.

SELECT COUNT(1) FROM ... WHERE ...
Adarshr
la source
1
SELECT 1 FROM ... WHERE ... ne serait-il pas encore plus rapide?
patrick
3
@patrick - SELECT 1 ...retournera autant de lignes que le WHEREet LIMITdemandera, et elles seront toutes "1".
Rick James
1
show table status like '<TABLE NAME>' Ce sera beaucoup plus rapide.
profond
@deep - mais pas pertinent si vous avez une WHEREclause. Et, pour InnoDB, ce n'est qu'une estimation.
Rick James
@RickJames oui c'est vrai!
profond
6

Si vous avez besoin d'obtenir le nombre de l'ensemble de résultats, vous pouvez adopter l'approche suivante:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

Ce n'est normalement pas plus rapide que l'utilisation, COUNTbien que l'on puisse penser que le contraire est le cas, car il fait le calcul en interne et ne renvoie pas les données à l'utilisateur, donc l'amélioration des performances est suspectée.

Faire ces deux requêtes est bon pour la pagination pour obtenir des totaux mais pas particulièrement pour utiliser des WHEREclauses.

Alex Rashkov
la source
Intéressant. Cela fonctionne-t-il dans les systèmes de base de données les plus courants? MySQL, Postgres, SQLite ...?
Franz
4
Ce n'est en fait souvent pas plus rapide que d'utiliser COUNT (*). Voir stackoverflow.com/questions/186588/…
toon81
2
Vous devez être TRÈS prudent lorsque vous utilisez cette fonction. Son utilisation imprudente a jadis mis un terme à tout notre environnement de production. Il est très gourmand en ressources, donc utilisez-le avec précaution.
Janis Peisenieks
6

J'ai fait quelques benchmarks pour comparer le temps d'exécution de COUNT(*)vs COUNT(id)(id est la clé primaire de la table - indexée).

Nombre d'essais: 10 * 1000 requêtes

Résultats: COUNT(*)est plus rapide 7%

VOIR GRAPHIQUE: benchmarkgraph

Mon conseil est d'utiliser: SELECT COUNT(*) FROM table

SamuelCarreira
la source
1
Pour info, il y a aussi un moyen commun de compter avec COUNT(1), il serait intéressant de voir quelques repères là-bas ...
Sliq le
4

Essaye ça:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";
bayuah
la source
@lepe Je suis désolé. Je veux dire, c'est vraiment bien si quelqu'un qui a voté contre lui explique pourquoi il / elle fait ça, afin que tout le monde puisse en apprendre quelque chose.
bayuah
1
Cela vous donnera rapidement une réponse approximative . Si vous avez besoin d'une réponse exacte, vous devez effectuer select count(*) from table_nameou autre chose. dba.stackexchange.com/questions/151769/…
Programster
@Programster Merci. C'est mieux que de me laisser dans le noir pendant presque un an.
bayuah
1
@bayuah Je ne sais pas ce que vous entendez par votre dernier commentaire. Je ne peux que supposer que c'est moi qui ai voté contre votre réponse, ce que je ne suis pas.
Programster
1
@Programster Non, je suis désolé, je ne voulais pas dire ça. Je voulais dire merci pour votre explication, donc je peux conjecturer ce que peut-être Downvoter pensait quand il / elle faisait ça.
bayuah
3

Vous voudrez peut-être envisager de faire un SELECT max(Id) - min(Id) + 1. Cela ne fonctionnera que si vos identifiants sont séquentiels et que les lignes ne sont pas supprimées. C'est cependant très rapide.

sky-dev
la source
3
Attention: les serveurs utilisent parfois une valeur d'incrémentation automatique> 1 (pour des raisons de sauvegarde), donc cette solution est bonne mais vous devez d'abord vérifier votre configuration de base de données.
Alex
1

EXPLAIN SELECT id FROM ....a fait l'affaire pour moi. et je pouvais voir le nombre de lignes sous la rowscolonne du résultat.

ssrp
la source
0

J'ai manipulé des tables pour le gouvernement allemand avec parfois 60 millions d'enregistrements.

Et nous devions connaître plusieurs fois le nombre total de lignes.

Ainsi, nous, les programmeurs de bases de données, avons décidé que dans chaque table se trouve l'enregistrement un toujours l'enregistrement dans lequel le nombre total d'enregistrements est stocké. Nous avons mis à jour ce nombre, en fonction des lignes INSERT ou DELETE.

Nous avons essayé toutes les autres méthodes. C'est de loin le moyen le plus rapide.

Scoobeedo Cool
la source
1
et quels sont les détails de la mise à jour de cette ligne? Ce qui signifie une conception défectueuse à une table, où toutes les lignes nécessiteraient un int gaspillé pour venir pour le trajet.
Tiré
5
Oui, c'est vraiment stupide haha. Avec chaque requête, vous devez ignorer la première ligne. Je créerais simplement un tableau des totaux et le remplirais en fonction d'un déclencheur. Table des utilisateurs sur insertion, mise à jour de la table des totaux. Table des utilisateurs sur suppression, mise à jour de la table des totaux.
HTMLGuy
-1

Une instruction count (*) avec une condition where sur la clé primaire a renvoyé le nombre de lignes beaucoup plus rapidement pour moi, évitant ainsi une analyse complète de la table.

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

C'était beaucoup plus rapide pour moi que

SELECT COUNT(*) FROM ...
ayakout
la source