[MySQL] optimisation (2 requêtes en une...) [4.0.12 final sortie]

optimisation (2 requêtes en une...) [4.0.12 final sortie] [MySQL] - SQL/NoSQL - Programmation

Marsh Posté le 17-03-2003 à 23:07:19    

Je suis en train de développer un moteur de recherche en PHP/MySQL.
Lors de l'affichage du résultat, j'affiche les 10 (ou 25 ou 50..) premier résultats ainsi que le nombre total de résultats.
 
Pour faire ceci, je fais dans un premier temps un :
SELECT COUNT(DISTINCT id) FROM ... WHERE ...
Pour connaître le nombre total de résultats puis un :
SELECT toto, tata, titi, ... FROM ... WHERE ... LIMIT 0, 10
Pour récupérer les 10 premier résultats pour les afficher.
 
Y a-t-il une meilleure manière de procéder que de faire ces 2 requêtes séparées ?
Je me demande s'il ne vaut mieux pas faire un :
SELECT toto, tata, titi, ... FROM ... WHERE XXX  
(sans limite donc)
De cette manière j'ai également le nombre total de résultat (mysql_num_rows()) mais je manipule un énorme array... En effet, ma base contient plusieurs centaines de milliers d'entrées...
 
Des idées ?


Message édité par ZeBorG le 19-03-2003 à 01:19:38
Reply

Marsh Posté le 17-03-2003 à 23:07:19   

Reply

Marsh Posté le 17-03-2003 à 23:33:14    

Je viens de testé avec la solution à 1 requête et effectivement, ça va plus vite.
Au lieu de fair une boucle sur ma deuxième requête comme ceci :
 
while ($row=mysql_fetch_row($query_result_handle)){
 echo $row[0], $row[1], ...
}
 
je fait ça :
 
mysql_data_seek ($query_result_handle, ($page - 1) * $nb_rows);
 
while ($nb_rows-- && $row=mysql_fetch_row($query_result_handle)){
 echo $row[0], $row[1], ...
}
 
mais est-ce-que l'utilisation mémoire va beaucoup augmenter ?


Message édité par ZeBorG le 17-03-2003 à 23:34:00
Reply

Marsh Posté le 18-03-2003 à 12:11:38    

Je suis moi aussi confronter au même problème.
Pour ma part j'avais opter pour la deuxième solution.

Citation :

mysql_data_seek ($query_result_handle, ($page - 1) * $nb_rows);  
 
while ($nb_rows-- && $row=mysql_fetch_row($query_result_handle)){  
echo $row[0], $row[1], ...  
}


Pour ce qui est de la question de l'utilisation de la mémoire, je pense que oui elle va augmenter. Mais je ne sais pas dans quel sens...

Reply

Marsh Posté le 18-03-2003 à 13:32:19    

ZeBorG a écrit :

Je suis en train de développer un moteur de recherche en PHP/MySQL.
Lors de l'affichage du résultat, j'affiche les 10 (ou 25 ou 50..) premier résultats ainsi que le nombre total de résultats.
 
Pour faire ceci, je fais dans un premier temps un :
SELECT COUNT(DISTINCT id) FROM ... WHERE ...
Pour connaître le nombre total de résultats puis un :
SELECT toto, tata, titi, ... FROM ... WHERE ... LIMIT 0, 10
Pour récupérer les 10 premier résultats pour les afficher.
 
Y a-t-il une meilleure manière de procéder que de faire ces 2 requêtes séparées ?
Je me demande s'il ne vaut mieux pas faire un :
SELECT toto, tata, titi, ... FROM ... WHERE XXX  
(sans limite donc)
De cette manière j'ai également le nombre total de résultat (mysql_num_rows()) mais je manipule un énorme array... En effet, ma base contient plusieurs centaines de milliers d'entrées...
 
Des idées ?


C'est une question intéressante :jap:
 
Je pense pas qu'on puisse donner de réponse générale, ca dépend des scripts, et du facteur le plus limitant sur ta machine (processeur ou RAM). Vu que le LIMIT BY ne fait au final qu'éviter un traitement supplémentaire post-requête par le script, dans la mesure où l'intégralité des lignes qui valident ta clause WHERE est parcourue (que ce soit au niveau des datas ou des index, peu importe), je pense qu'il est dommage de faire une deuxième requête qui va grosso-modo être traitée de la même manière par MySQL.  
 
Il vaut peut être mieux récupérer toutes les données. En détruisant l'array dès qu'il n'est plus utilisé ca doit pas consommer grand chose comme mémoire. Tout dépend des scripts après bien sur mais bon.
 
L'idéal serait évidement qu'il y ait une fonction peu documentée qui permette d'obtenir le nombre de lignes qui auraient été retournées par ta requête si tu n'avais pas mis de LIMIT, en plus du mysql_num_rows, mais on peut toujours rêver :D
 
Ce serait à proposer au staff de MySQL AB, car c'est quelque chose qui servirait pas mal.


Message édité par Core 666 le 18-03-2003 à 17:20:16
Reply

Marsh Posté le 18-03-2003 à 14:33:34    

En gros d'après ce que tu dis Core 666, il faudrait faire du PHP objet...

Reply

Marsh Posté le 18-03-2003 à 16:02:01    

Ah quoi tu penses plus précisement ? Je suis pas certain de voir de quelle manière le PHP objet peut apporter un gain de perfs à ce niveau ?

Reply

Marsh Posté le 18-03-2003 à 17:18:00    

Je ne sais pas exactement, je ne me suis pas très approfondie dans le PHP objet, mais je me demande si par ce moyen, on peut optimiser la chose.
Ce n'était qu'une question. Une idée, c'est à voir.
 [:spir]

Reply

Marsh Posté le 18-03-2003 à 17:42:03    

J'ai pu tester ma 2nd solution sur une machine moins performante que la mienne et voici les résultats :
 
Apparament, pour les recherches qui n'ont pas beaucoup de résultats, la 2nd solution est près de 2x plus rapide.
 
Par contre, si la recherche ratisse large et renvoie beaucoup de réponses, la recherche est plus longue et la machine moins puissante me dit qu'il y a moins de réponse qu'elle ne devrait !
Apparemens, la limitation en perf font qu'elle n'arrive pas à gérer une séléction aussi grande (~65000 entrées).
 
Je suis donc obligé de repassé à la solution à 2 requêtes à moins que qq trouve une autre solution ?
 
[EDIT] En fait sur ma machine moins puissante, la base n'a apparemment pas était mis à jours intégralement d'où le nombre moindre de résultats :whistle:  
Par contre, la 2nd solution prend quand même plus longtemps quant il y a beaucoup de résultats.


Message édité par ZeBorG le 19-03-2003 à 16:48:51
Reply

Marsh Posté le 18-03-2003 à 17:47:39    

ZeBorG a écrit :

J'ai pu tester ma  
Par contre, si la recherche ratisse large et renvoie beaucoup de réponses, la recherche est plus longue


Ce n'est pas normal. Tu as moyen de vérifier si c'est la requête qui est plus longue ou si c'est le traitement en PHP ? Avec un show processlist en shell par exemple ?

Reply

Marsh Posté le 19-03-2003 à 01:17:54    

Core 666 a écrit :


Ce n'est pas normal. Tu as moyen de vérifier si c'est la requête qui est plus longue ou si c'est le traitement en PHP ? Avec un show processlist en shell par exemple ?

Non, ce n'est pas le traitement PHP.
Je manipule une BD relativement grande (plusieurs centaines de milliers d'entrées...)
 
De toute façon mon problème est résolut avec MySQL 4.0.12 qui viens juste d'être affiché comme étant "Production-Ready" (= ce n'est plus une bêta)
 
Si tu lis là :
http://www.mysql.com/doc/en/Nutshe [...] tures.html
Ils disent: "Functions like SQL_CALC_FOUND_ROWS and FOUND_ROWS() make it possible to know how many rows a query would have returned without a LIMIT clause."
Et tu sais ce que cela veut dire...

Reply

Marsh Posté le 19-03-2003 à 01:17:54   

Reply

Marsh Posté le 19-03-2003 à 01:35:33    

Pour utiliser cette nouvelle fonctionnalité (uniquement dans 4.x) il faut procéder comme ceci :
 
$sql = "SELECT SQL_CALC_FOUND_ROWS * FROM table WHERE ... LIMIT 0, 50";
$result = mysql_query($sql);
 
$sql = "SELECT FOUND_ROWS()";
$count_result = mysql_query($sql);
 
De cette manière, MySQL n'effectue qu'une fois la requête. Le second mysql_query() ne fait que récupérer une valeur calculé précédemment.

Reply

Marsh Posté le 19-03-2003 à 17:14:19    

En fait, pour mon problème bizarre où j'obtenais moins de résultats avec la 2nd solution, c'est par ce que la base sur le vieille machine n'était pas à jours :whistle:  
Par contre la 2nd solution est quand même plus lente quant il y a beaucoup de résultats.
 
De toute façon mon problème est résolu avec mySQL 4.
 
Et pour être compatible mySQL 3.x je procède de la manière suivante :

Code :
  1. mysql_connect(HOST, USERNAME, PASSWORD) or die(mysql_error());
  2. @mysql_select_db(DATABASE) or die('Unable to select database');
  3. $mysql_version = mysql_result(mysql_query('SELECT VERSION()'), 0);
  4. if ($mysql_version[0]>= 4)
  5.    $sql_query = "SELECT SQL_CALC_FOUND_ROWS (...)" . $sql_from_query . $sql_where_query;
  6. else {
  7.    $sql_query_count = "SELECT COUNT(DISTINCT table.id)" . $sql_from_query . $sql_where_query;
  8.    $sql_query = "SELECT (...)" . $sql_from_query . $sql_where_query;
  9. }
  10. $sql_query .= " LIMIT " . ($page - 1) * $nb_rows . ", " . $nb_rows;
  11. (...)
  12. if ($mysql_version[0]>= 4){
  13.    $query_result_handle = mysql_query($sql_query);
  14.    $nb_results = mysql_result(mysql_query('SELECT FOUND_ROWS()'), 0);
  15. }
  16. else{
  17.    $query_result_handle = mysql_query($sql_query_count);
  18.    $nb_results = mysql_result($query_result_handle, 0);
  19. }
  20. (...)
  21. if ($nb_results == 0) {
  22.    echo 'no results';
  23.    mysql_close();
  24.    include 'footer.php';
  25.    exit();
  26. }
  27. if ($mysql_version[0]< 4)
  28.    $query_result_handle = mysql_query($sql_query);
  29. (...)
  30. mysql_close();
  31. include 'footer.php';

Voila, en espérant que ça peu aider qq :wahoo:


Message édité par ZeBorG le 19-03-2003 à 17:18:20
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed