Différence entre ...

Statut
N'est pas ouverte pour d'autres réponses.

La Poubelle

Pou'r allé Danché
Calvin80 a dit:
je ne te suis plus..
on ne compare pas select count(*) et select *, on compare select count(*) et select count(champ) et , d'un autre coté, select * et select champ1, champ2 etc...
temps_executiont(count *) <= temps_execution (count champ)
et
temps execution (select *) >= temps_execution (select champ1,champ2,...)
Un select count(ID) est tout aussi rapide qu'un select count(*).

Pour la généralisation à un simple SELECT * FROM TABLE, c'était suite à :

La question initale qui demandait COUNT(*) vs SELECT * FROM TABLE avecc un traitement dans PHP.

Et surtout:

C'est en fait le contraire qu'il dit : un count(*) est toujours plus rapide qu'un count(nom_d'un_champ). Maintenant dans le cas d'une requete select normale (ou on veut des lignes de la DB, et pas le nombre de ligne), il est effectivement préférable de récupérer juste les champs dont on a besoin plutot que tous (*), pour limiter le nombre de données transmises entre la DB et le serveur web. Mais même dans ce cas la, un "select * from ..." sera plus rapide qu'un "select champ1, champ2 from ...".
Tifox l'hérétique. ;)


Je l'avoue, le post a dévié sur l'utilisation de l'instruction *, autant faire une petite discution intéressante la dessus :mrgreen:
 

Tifox

ou pas
Voila, je viens d'effectuer plein de petits test, sur des DB mySQL, SQL Server et DB2. La table contenait 80 champs de tout types, dont certain a null aléatoirement et un id int. J'ai charger 5 millions d'entrée pour un total de plus de 15Go de données.

Dans tout les cas, le count(*) a été plus rapide (un poil ou beaucoup) que count(id) ou count (nom_d'un_champs).

Le select maintenant, la c'est plus complexe. Un select(*) ne lui même est plus rapide qu'un select(champs1, champs 2, ...) d'après ce que j'ai vu sur DB2, mais je n'ai pas su évaluer la chose sur les serveurs SQL car je n'ai pas trouvé comment faire un select sans afficher les résultats (ce qui n'a d'ailleurs aucun intérêt). Vu que ces résultats contiennent parfois des Go de données, c'est forcément ça qui ralentit tout. Donc pour le select, si l'opération en elle même semble plus rapide avec un select(*), vu qu'il faut de toute façon rapatrier les donnée quelques part, il vaudra toujours mieux ne rapatrier que les champs dont a réellement besoin.
 
1er
OP
SkYlEsS

SkYlEsS

Elite
Et entre count(*) et mysql_num_rows ? :-D
 

Bingo

Beer Addict
La Poubelle a dit:
Dans un select classique, Sur une BDD d'une cinquante de champ (bien chargé en string, pour faire court). Entre "SELECT * FROM TBL" et "SELECT ID, CHAMP1 from TBL". Il est impossible que le serveur n'en prenne pas un coup au niveau de la charge et de la mémoire.
Je pense que sur certains SGBD (PotgreSQL par exemple, je ne peux pas généraliser à d'autres que je connais moins), c'est seulement les accès disque qui vont être plus intenses au moment de l'envoi du résultat de la requête.
PostgreSQL récupère d'abord une liste des identifiants des enregistrements à renvoyer. (ces identifiants sont des valeurs internes au serveur, ce ne sont pas les clés primaires)
Il trie cette liste pour la classer soit dans l'ordre du ORDER BY si il existe, soit dans l'ordre physique des enregistrements sur le disque si il n'y a pas de tri.
Ensuite il "fetch" les enregistrements en parcourant la table dans l'ordre déterminé précédemment, et à chaque enregistrement il lit les champs demandé.

Exemple sur une table d'environ 30 millions d'enregistrement avec une requête qui en renvoie environ 300.000 :

Avec deux champs sélectionnés :

EXPLAIN ANALYZE
SELECT name, nw_geometry
FROM nw
WHERE frc=1;


"Bitmap Heap Scan on nw (cost=5716.58..692820.62 rows=305210 width=171) (actual time=1491.495..665969.803 rows=314909 loops=1)"
" Recheck Cond: (frc = 1)"
" -> Bitmap Index Scan on nw_frc_btree (cost=0.00..5640.28 rows=305210 width=0) (actual time=1383.121..1383.121 rows=314909 loops=1)"
" Index Cond: (frc = 1)"
"Total runtime: 666065.283 ms"

Avec tous les champs sélectionnés (en gros, le double en largeur d'enregistrement) :

EXPLAIN ANALYZE
SELECT *
FROM nw
WHERE frc=1;


"Bitmap Heap Scan on nw ( cost=5716.58..692820.62 rows=305210width=354) (actual time=473.733..253735.608 rows=314909 loops=1)"
" Recheck Cond: (frc = 1)"
" -> Bitmap Index Scan on nw_frc_btree (cost=0.00..5640.28 rows=305210 width=0) (actual time=386.953..386.953 rows=314909 loops=1)"
" Index Cond: (frc = 1)"
"Total runtime: 253830.356 ms"


Pour PostgreSQL, aucune différence au niveau du coût. Au niveau du serveur, j'ai constaté une légère augmentation des accès mémoire, mais pas d'augmentation de la mémoire utilisée.


Toujours rien à voir avec le message originel, mais c'était pour faire avancer l'autre schmilblick ! ;)
 

Bingo

Beer Addict
Tifox a dit:
Dans tout les cas, le count(*) a été plus rapide (un poil ou beaucoup) que count(id) ou count (nom_d'un_champs).
COUNT(*) sera systématiquement plus rapide, parce qu'il n'a pas la même signification que Count(field).
COUNT(*) a une signification spéciale : il renvoie la cardinalité de la table, c'est à dire le nombre de lignes.
COUNT(field) renvoie le nombre d'enregistrements où field est non nul, ça revient à "COUNT(*) WHERE field IS NOT NULL".

C'est plus long parce que pas mal de moteurs de bases de données (comme MySQL) cachent le nombre de lignes d'une table, ils n'ont donc pas besoin d'éxécuter la requête. Pour COUNT(id), si il ne sont pas transactionnels et qu'il y a un index sur id, ils se contentent de compter les enregistrements dans l'index, ça reste donc très rapide.
Pas mal de SGBD transactionnels ont aussi des informations de transaction dans les index, du coup ils peuvent aussi optimiser les COUNT.

PostgreSQL est un cas très spécial dans le monde des SGBD car il ne cache pas le nombre de lignes. C'est dû au fait qu'il soit fortement axé sur les transactions.
Ses index ne contiennent pas d'information de transaction, donc PostgreSQL ne peut pas se baser sur eux pour compter ni le nombre de lignes, ni le nombre de lignes non nulles : il est obligé de parcourir toute la table !
Conséquence directe : COUNT(*) est très lent dans PostgreSQL, et prend exactement le même temps que COUNT(field).

EXPLAIN ANALYZE
SELECT COUNT(*)
FROM nw
WHERE frc=1;


"Aggregate (cost=693583.64..693583.65 rows=1 width=0) (actual time=335830.861..335830.861 rows=1 loops=1)"
" -> Bitmap Heap Scan on nw (cost=5716.58..692820.62 rows=305210 width=0) (actual time=447.847..335715.959 rows=314909 loops=1)"
" Recheck Cond: (frc = 1)"
" -> Bitmap Index Scan on nw_frc_btree (cost=0.00..5640.28 rows=305210 width=0) (actual time=361.512..361.512 rows=314909 loops=1)"
" Index Cond: (frc = 1)"
"Total runtime: 335831.777 ms"



EXPLAIN ANALYZE
SELECT COUNT(gid)
FROM nw
WHERE frc=1;


"Aggregate (cost=693583.64..693583.65 rows=1 width=4) (actual time=539638.289..539638.290 rows=1 loops=1)"
" -> Bitmap Heap Scan on nw (cost=5716.58..692820.62 rows=305210 width=4) (actual time=1074.860..539482.828 rows=314909 loops=1)"
" Recheck Cond: (frc = 1)"
" -> Bitmap Index Scan on nw_frc_btree (cost=0.00..5640.28 rows=305210 width=0) (actual time=988.216..988.216 rows=314909 loops=1)"
" Index Cond: (frc = 1)"
"Total runtime: 539639.177 ms"

COUNT(gid) reste un peu plus cher, parce qu'il faut regarder la valeur du champ pour savoir si il est nul, d'où le "width=4", alors qu'avec COUNT(*) width=0.
 

Tifox

ou pas
Et bien voila, ont sait tout sur le count() :p
 

oNi-

:baille: Lecteur
SkYlEsS a dit:
Et entre count(*) et mysql_num_rows ? :-D
mysql_num_rows n'est pas du SQL.

Ta question à la base était de savoir s'il était plus efficace/rapide de faire un COUNT ou, un SELECT suivi de la fonction php mysql_num_rows() sur le résultat du SELECT.
La réponse est celle que cvm t'a fournie, à savoir le COUNT.

Maintenant, ça ne veut pas dire qu'il faut refaire un COUNT si tu as déjà fait un SELECT bien entendu.
 
1er
OP
SkYlEsS

SkYlEsS

Elite
Mais si je veux compter le nombre d'id et en même temps récupérer des infos, que vaut-il mieux faire ?

SELECT champ_1, champ_2 COUNT(*) FROM table WHERE ...

ou

SELECT COUNT(*) FROM table WHERE ... suivi de SELECT champ_1, champ_2 FROM table WHERE ...

:-D
 

Bingo

Beer Addict
SkYlEsS a dit:
Mais si je veux compter le nombre d'id et en même temps récupérer des infos, que vaut-il mieux faire ?

SELECT champ_1, champ_2 COUNT(*) FROM table WHERE ...

ou

SELECT COUNT(*) FROM table WHERE ... suivi de SELECT champ_1, champ_2 FROM table WHERE ...

:-D
En MySQL (je ne sais pas à partir de quelle version), tu peux faire :
SELECT SQL_CALC_FOUND_ROWS champ_1, champ_2 FROM table WHERE ...;
Puis :
SELECT FOUND_ROWS();

Mais tu dois faire attention à ce qu'aucune autre requête n'ait été exécutée dans cette connexion avant ton SELECT FOUND_ROWS(), car ça renvoie le nombre de lignes dans la dernière requête exécutée sur cette connexion.

Par contre, c'est très pratique si tu veux faire un SELECT ... LIMIT xx , parce qu'il te donne le nombre de lignes au total. Du coup c'est plus facile de faire des trucs du style "résultats 11 à 20 sur 138".
 

La Poubelle

Pou'r allé Danché
Pas mal le système de PostgreSQL. Ca a l'air d'être un bon système de BDD pour un serveur WEB public quand on ne connait pas le niveau des utilisateurs ainsi que l'utilisation qu'ils vont faire.

Ca évite des carnages mémoires.

Thanks pour l'info =]
 

oNi-

:baille: Lecteur
SkYlEsS a dit:
Mais si je veux compter le nombre d'id et en même temps récupérer des infos, que vaut-il mieux faire ?

SELECT champ_1, champ_2 COUNT(*) FROM table WHERE ...

ou

SELECT COUNT(*) FROM table WHERE ... suivi de SELECT champ_1, champ_2 FROM table WHERE ...

:-D
Ce n'est pas du tout pareil :
Dans le premier cas, il te faut faire un group by et tu auras dans la colonne du COUNT(*) le nombre d'enregistrements ayant les mêmes informations que cette ligne
Exemple trivial:
Code:
SELECT COUNT(*), id_contact FROM contact WHERE titre ='Madame' GROUP BY id_contact;
te donnera
1 5
1 15
1 22
etc
donc comme on groupe sur l'id (clé primaire) et qu'il est unique, tu auras toujours 1 dans la colonne du COUNT(*).

Maintenant si tu fais un group by su un champ qui n'est pas obligatoirement unique, là tu pourrais avoir par exemple :
Code:
SELECT COUNT(*), prenom FROM contact WHERE titre ='Madame' GROUP BY prenom;
qui te donnerait :
2 Agnès
1 Anne
3 Catherine
etc

Voilà pour le premier cas.

Alors que dans l'autre :
Code:
SELECT COUNT(*) FROM contact WHERE titre ='Madame';
te donnera 1 seule ligne contenant le nombre ( x ) d'enregistrements que tu trouveras dans le résultat du SELECT que tu vas effectuer juste après :
Code:
SELECT id_contact, etc FROM contact WHERE titre ='Madame';
qui te fournira donc x lignes (résultat du COUNT précédent donc) avec les colonnes sélectionnées.


EDIT, en français ça donne un peu mieux :
Réponse : tout dépend de ce que tu cherches à faire, savoir le nombre de lignes renvoyées par le SELECT ou connaître le nombre d'occurences pour une même valeur.

EDIT² : Donc le 2eme cas ne sert à rien, vu que la réponse du COUNT avant le SELECT peut être fourni par mysql_num_rows() sur le résultat du SELECT.
 

Bingo

Beer Addict
La Poubelle a dit:
Pas mal le système de PostgreSQL. Ca a l'air d'être un bon système de BDD pour un serveur WEB public quand on ne connait pas le niveau des utilisateurs ainsi que l'utilisation qu'ils vont faire.
Ca évite des carnages mémoires.
Thanks pour l'info =]
PostgreSQL est excellent en TOUT, SAUF pour :
- COUNT(*)
- MAX() ou MIN() (mais résolu depuis la version 8.0)

Pour le COUNT(*), je ne vois pas bien comment ils pourraient résoudre ça. A part en ajoutant les informations de transaction aux index, mais ce serait un changement profond, donc peu probable.
 
1er
OP
SkYlEsS

SkYlEsS

Elite
Quelle est la différence entre mysql_fetch_assoc et mysql_fetch_array ?

L'un s'emploie-t-il alors que l'autre ne s'emploie pas dans quels cas ?

Que faut-il mieux faire pour un simple SELECT par exemple ?

:=)
 

oNi-

:baille: Lecteur
mysql_fetch_array() : donne un tableau indexé sur le nom des champs et un offset (base 0) **
mysql_fetch_assoc() : donne un tableau indexé sur le nom des champs
mysql_fetch_row() : donne un tableau indexé par un offset (base 0)

parait que le plus rapide est _row.
y en a encore d'autres...

**EDIT : $data['nom_champ'] ou $data[0].
 
1er
OP
SkYlEsS

SkYlEsS

Elite
Est-ce une mauvaise manie de faire un $data['0'] au lieu d'un $data[0] ?
 

oNi-

:baille: Lecteur
SkYlEsS a dit:
Est-ce une mauvaise manie de faire un $data['0'] au lieu d'un $data[0] ?
A priori l'emploi des quotes c'est pour les tableaux associatifs.
Ici le 0 est l'indice, donc je dirais oui :)
 

moustic

Touriste
J'arrive peut-être en plein milieu de la discussion mais quand ont fait un select * on a en retour un pointeur sur la première ligne qui contient le ou les critère de la requete, les données elles même ne sont retournée que lorsque l'on y accede.

Donc à mon avis pas de différence entre un select * et un select champ1, champ2,...
 

Tifox

ou pas
moustic a dit:
J'arrive peut-être en plein milieu de la discussion mais quand ont fait un select * on a en retour un pointeur sur la première ligne qui contient le ou les critère de la requete, les données elles même ne sont retournée que lorsque l'on y accede.

Donc à mon avis pas de différence entre un select * et un select champ1, champ2,...
Tu es sur de ça ? Ca me parait bizarre, car ça sous-entend qu'il y a au moins 2 appel réseau à la BD, une pour avoir le pointeur, et l'autre pour avoir les données. Sans compter que si c'est effectivement séparer, il faut gérer les accès concurrent vu que le select et l'accès des données ne sont pas toujours suivi, donc risque de modification des données par un tiers.
 

neku

Codeur roumain
moustic a dit:
J'arrive peut-être en plein milieu de la discussion mais quand ont fait un select * on a en retour un pointeur sur la première ligne qui contient le ou les critère de la requete, les données elles même ne sont retournée que lorsque l'on y accede.

Donc à mon avis pas de différence entre un select * et un select champ1, champ2,...
Je confirme que pour MySQL le retour d'un select est un pointeur sur les données et les données sont récupérées lors de l'accès.
Maintenant il est possible de stocker directement la totalité des données en RAM pour ne pas devoir récupérer les données à chaque accès.

Maintenant à savoir si php stock ou pas ...
 
Statut
N'est pas ouverte pour d'autres réponses.
Haut