Question

Je sais que c'est généralement une mauvaise idée de faire des requêtes comme celle-ci:

SELECT * FROM `group_relations`

Mais quand je veux juste le nombre, devrais-je choisir cette requête car cela permet à la table de changer mais donne toujours les mêmes résultats.

SELECT COUNT(*) FROM `group_relations`

Ou le plus spécifique

SELECT COUNT(`group_id`) FROM `group_relations`

J'ai le sentiment que ce dernier pourrait potentiellement être plus rapide, mais y a-t-il autre chose à considérer?

Mise à jour : J'utilise InnoDB dans ce cas. Désolé de ne pas être plus spécifique.

Était-ce utile?

La solution

Si la colonne en question n'est pas NULL, vos deux requêtes sont équivalentes. Lorsque group_id contient des valeurs NULL,

select count(*)

comptera toutes les lignes, alors que

select count(group_id)

ne comptera que les lignes pour lesquelles id_groupe n'est pas nul.

En outre, certains systèmes de base de données, tels que MySQL, utilisent une optimisation lorsque vous demandez le nombre (*), ce qui rend ces requêtes un peu plus rapides que celles spécifiques.

Personnellement, lorsque je compte, je compte (*) pour être du côté des nuls.

Autres conseils

Si je me souviens bien, MYSQL COUNT (*) compte toutes les lignes, alors que COUNT (nom_colonne) ne comptabilise que les lignes qui ont une valeur non NULL dans la colonne donnée.

COUNT (*) compte toutes les lignes alors que COUNT (nom_colonne) ne compte que les lignes sans valeur NULL dans la colonne spécifiée.

Important à noter dans MySQL:

COUNT () est très rapide sur les tables MyISAM pour les colonnes * ou non-null, car le nombre de lignes est mis en cache. InnoDB ne met pas en cache le nombre de lignes, il n'y a donc aucune différence de performances pour COUNT (*) ou COUNT (nom_colonne), que la colonne soit vide ou non. Vous pouvez en savoir plus sur les différences sur cet article à l'adresse suivante: Blog de performance MySQL.

si vous essayez SELECT COUNT (1) FROM groupe_relations, ce sera un peu plus rapide car il ne tentera pas de récupérer les informations de vos colonnes.

Edit: Je viens de faire quelques recherches et de découvrir que cela ne se produit que dans certains db. Dans sqlserver, utiliser 1 ou * est identique, mais sous Oracle, il est plus rapide d’utiliser 1.

http: //social.msdn.microsoft.com/forums/en-US/transactsql/thread/9367c580-087a-4fc1-bf88-91a51a4ee018/

Apparemment, il n'y a pas de différence entre eux dans mysql, comme pour sqlserver, l'analyseur semble changer la requête en select (1). Désolé si je vous induis en erreur d'une certaine manière.

J'étais curieux de cela moi-même. C'est bien de lire de la documentation et des réponses théoriques, mais j'aime bien équilibrer celles-ci avec des preuves empiriques.

J'ai une table MySQL (InnoDB) contenant 5 607 997 enregistrements. La table est dans mon propre bac à sable privé, donc je sais que le contenu est statique et que personne d'autre n'utilise le serveur. Je pense que cela supprime efficacement tous les effets extérieurs sur les performances. J'ai une table avec un champ auto_increment Primary Key (Id) qui, je le sais, ne sera jamais nulle et que je vais utiliser pour mon test de clause where (WHERE Id IS NOT NULL).

Le seul autre problème possible que je vois dans les tests en cours est le cache. La première fois qu'une requête est exécutée sera toujours plus lente que les requêtes suivantes qui utilisent les mêmes index. J'appellerai cela ci-dessous l'appel de mise en mémoire cache. Juste pour mélanger un peu, je l’ai couru avec une clause where dont je sais qu’elle aura toujours la valeur true, quelles que soient les données (TRUE = TRUE).

Cela dit voici mes résultats:

Type de requête

      |  w/o WHERE          | where id is not null |  where true=true

COUNT ()

      |  9 min 30.13 sec ++ | 6 min 16.68 sec ++   | 2 min 21.80 sec ++
      |  6 min 13.34 sec    | 1 min 36.02 sec      | 2 min 0.11 sec 
      |  6 min 10.06 se     | 1 min 33.47 sec      | 1 min 50.54 sec

COUNT (Id)

      |  5 min 59.87 sec    | 1 min 34.47 sec      | 2 min 3.96 sec 
      |  5 min 44.95 sec    | 1 min 13.09 sec      | 2 min 6.48 sec

COUNT (1)

      | 6 min 49.64 sec    | 2 min 0.80 sec       | 2 min 11.64 sec
      | 6 min 31.64 sec    | 1 min 41.19 sec      | 1 min 43.51 sec

++ Ceci est considéré comme l'appel d'amorçage en cache. Il devrait être plus lent que les autres.

Je dirais que les résultats parlent d’eux-mêmes. COUNT (Id) contourne généralement les autres. L'ajout d'une clause Where diminue considérablement le temps d'accès, même s'il s'agit d'une clause que vous évaluerez comme étant vraie. Le sweet spot semble être COUNT (Id) ... O Id n’est pas nul.

J'aimerais beaucoup voir les résultats d'autres personnes, peut-être avec des tables plus petites ou avec des clauses where contre des champs différents du champ que vous comptez. Je suis sûr qu'il y a d'autres variantes que je n'ai pas prises en compte.

Rechercher des alternatives

Comme vous l'avez vu, lorsque les tables grossissent, les requêtes COUNT deviennent lentes. Je pense que le plus important est de considérer la nature du problème que vous essayez de résoudre. Par exemple, de nombreux développeurs utilisent des requêtes COUNT lors de la génération d'une pagination pour de grands ensembles d'enregistrements afin de déterminer le nombre total de pages du jeu de résultats.

Sachant que les requêtes COUNT vont ralentir, vous pouvez envisager un autre moyen d'afficher les contrôles de pagination qui vous permet simplement de contourner la requête lente. La pagination de Google est un excellent exemple.

Dénormaliser

Si vous devez absolument connaître le nombre d'enregistrements correspondant à un nombre spécifique, utilisez la technique classique de dénormalisation des données. Au lieu de compter le nombre de lignes au moment de la recherche, envisagez d’incrémenter un compteur lors de l’insertion d’un enregistrement et de le décrémenter lors de la suppression d’un enregistrement.

Si vous décidez de le faire, envisagez d'utiliser des opérations transactionnelles idempotentes pour maintenir ces valeurs dénormalisées synchronisées.

BEGIN TRANSACTION;
INSERT INTO  `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;

Vous pouvez également utiliser des déclencheurs de base de données si votre SGBDR les prend en charge.

En fonction de votre architecture, il peut être judicieux d’utiliser une couche de mise en cache telle que memcached pour stocker, incrémenter et décrémenter la valeur dénormalisée et simplement passer à la requête compte lent lorsque la clé de cache est manquante. Cela peut réduire les conflits d’écriture globaux si vous avez des données très volatiles, mais dans ce cas, vous devrez envisager solutions à l'effet de pile de poils .

Les tables MySQL ISAM doivent avoir une optimisation pour COUNT (*), évitant ainsi l’analyse complète des tables.

Un astérisque dans COUNT n'a pas d'incidence pour la sélection de tous les champs de la table. C’est de la foutaise de dire que COUNT (*) est plus lent que COUNT (champ)

Je pense que sélectionner COUNT (*) est plus rapide que sélectionner COUNT (champ). Si le SGBDR a détecté que vous spécifiez " * " sur COUNT au lieu du champ, il n'est pas nécessaire d'évaluer quoi que ce soit pour incrémenter le compte. Alors que si vous spécifiez un champ sur COUNT, le SGBDR évaluera toujours si votre champ est nul ou non pour le compter.

Mais si votre champ est nullable, spécifiez le champ dans COUNT.

COUNT (*) faits et mythes:

MYTHE : "InnoDB ne gère pas correctement les requêtes de comptage (*)":

La plupart des requêtes count (*) sont exécutées de la même manière par tous les moteurs de stockage si vous avez une clause WHERE, sinon InnoDB devra effectuer une analyse complète de la table.

FACT : InnoDB n'optimise pas le nombre de requêtes count (*) sans la clause where

Il est préférable de compter par une colonne indexée telle qu'une clé primaire.

SELECT COUNT(`group_id`) FROM `group_relations`

Cela devrait dépendre de ce que vous essayez réellement de réaliser, comme Sebastian l’a déjà dit, c’est-à-dire préciser vos intentions! Si vous êtes simplement en train de compter les lignes, optez pour le compte (*) ou comptez une seule colonne pour le compte (colonne).

Cela pourrait également valoir la peine de consulter votre fournisseur de base de données. Auparavant, lorsque j'utilisais Informix, il avait une optimisation pour COUNT (*), dont le coût d’exécution du plan de requête était de 1 par rapport au comptage de colonnes uniques ou multiples, ce qui donnerait un chiffre plus élevé

  

si vous essayez SELECT COUNT (1) FROM group_relations, ce sera un peu plus rapide car il ne tentera pas de récupérer les informations de vos colonnes.

COUNT (1) était plus rapide que COUNT (*), mais ce n'est plus le cas, car les SGBD modernes sont suffisamment intelligents pour savoir que vous ne voulez pas connaître les colonnes

Le conseil que m'a donné MySQL à propos de choses telles que celle-ci est qu'en général, essayer d'optimiser une requête en se basant sur de tels trucs peut être une malédiction à long terme. Il existe des exemples dans l'histoire de MySQL où la technique hautes performances de quelqu'un reposant sur le fonctionnement de l'optimiseur finit par constituer le goulot d'étranglement dans la prochaine version.

Écrivez la requête qui répond à la question que vous posez. Si vous souhaitez un décompte de toutes les lignes, utilisez COUNT (*). Si vous voulez un nombre de colonnes non NULL, utilisez COUNT (col) WHERE col IS NOT NULL. Indexez correctement et laissez l'optimisation à l'optimiseur. Essayer de créer vos propres optimisations au niveau de la requête peut parfois rendre l’optimiseur intégré moins efficace.

Cela dit, une requête peut permettre à l'optimiseur de l'accélérer plus facilement, mais je ne crois pas que COUNT en fait partie.

Modifier: Les statistiques dans la réponse ci-dessus sont intéressantes, cependant. Je ne suis pas sûr qu'il y ait réellement quelque chose à l'œuvre dans l'optimiseur dans ce cas. Je parle simplement des optimisations au niveau de la requête en général.

  

Je sais que c'est généralement une mauvaise idée de faire   des requêtes comme celle-ci:

SELECT * FROM `group_relations`
     

Mais quand je veux juste le compte, devrait   Je vais pour cette requête car cela permet   la table pour changer mais donne toujours   les mêmes résultats.

SELECT COUNT(*) FROM `group_relations`

Comme votre question l'indique, la raison pour laquelle SELECT * est mal avisée est que des modifications de la table peuvent nécessiter des modifications de votre code. Cela ne s'applique pas à COUNT (*) . Il est assez rare de vouloir le comportement spécialisé que SELECT COUNT ('group_id') vous donne - en règle générale, vous souhaitez connaître le nombre d'enregistrements. C’est à cela que sert COUNT (*) , utilisez-le donc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top