50 000 enregistrements avec PHP MySql, extremement lent

Eléphant du PHP | 64 Messages

13 avr. 2006, 02:18

Bonjour et merci à ceux et celles qui prennent du temps pour mon problème.

Mon problème:
Temps de recherche allant jusqu'à plusieurs minutes, et donnant parfois un message d'erreur

J'utilise de nombreuses requêtes du genre

<?php
session_start();
if ($v_adr_id0 == '') {$v_adr_id0=$adr_id;} else {$adr_id=$v_adr_id0;}
if ($v_adp_id0 == '') {$v_adp_id0=$adp_id;} else {$adp_id=$v_adp_id0;}

//echo "line 6 edit0 $edit0 v_edit0 $v_edit0<br>";
$v_edit0=$edit0;

if ($name0 != '' ) {

    $name0 = strtoupper($name0);
    if (strpos($name0,' ')>0) {$name0 = substr($name0,0,strpos($name0,' '));}
    if (strpos($name0,',')>0) {$name0 = substr($name0,0,(strpos($name0,' ')-1));}
    if (strlen($name0) > 10) {$name0 = substr($name0,0,10);}

    }
//echo "line 9 edit0 $edit0 v_edit0 $v_edit0<br>";

//echo "adr_id $adr_id v_adr_id0 $v_adr_id0 adp_id $adp_id v_adp_id0 $v_adp_id0<br>";

?>
<html>
<head>
<title>Name search Business Dashboard</title>
</head>
<body link="blue" alink="blue" vlink="blue">

<?php

    echo "<table>";

    echo "<tr>";

    echo "<td bgcolor=silver align=left valign=top><img src=\"images_CRM/dbd_120.gif\" width=\"128\" height=\"79\" alt=\"Business Dashboard\"></td>\n";

    echo "<td align=center bgcolor=\"#66FF99\"><font face=Arial size=3>BUSINESS<br>DASHBOARD</font><br><b><font face=Arial color=\"#FFFFFF\" size=2>Management</font></b><td>";

?>

  <form method="post" action="<?php echo $PHP_SELF?>">

  <input type="Text" name="name0" size="20" value="">

  <input type="Submit" name="submit" value="Search this name">

  </form>

<?php

echo "</table>";

if ($name0 !='') {

echo "<table border=1>";

echo "<tr><td align=center colspan=2><font face=Arial size=4>Search for $name0";

include "../include/get_root_da_erp.inc";

echo "<tr><td valign=top><table border=0><tr><td colspan=2 align=center><font face=Arial size=3>Searching in Addresses";

$result = mysql_query ("SELECT * FROM address",$db);

	while ($myrow = mysql_fetch_array($result)) {

        $v_descr1 = strtoupper('@'.$myrow["adr_name1"]);
        $v_descr2 = strtoupper('@'.$myrow["adr_name2"]);

        if ( strpos($v_descr1,$name0) > 0 || strpos($v_descr2,$name0) > 0 ) {

        $v_name1_10 = substr($myrow["adr_name1"],0,10);

        printf("<tr><td><font size=1>%s</font><td><a href=\"address.php?search_10=$v_name1_10\"><font size=2>%s %s</font></a>\n", $myrow["adr_id"], $myrow["adr_name1"], $myrow["adr_name2"]);

        } 

        }

echo "</table></td><td valign=top>";

echo "<table border=0><tr><td colspan=3 align=center><font face=Arial size=3>Searching in Contacts";

$resultct = mysql_query ("SELECT * FROM adtel",$db);

	while ($myrowct = mysql_fetch_array($resultct)) {

        $v_descr3 = strtoupper('@'.$myrowct["adt_first"]);
        $v_descr3sp = strpos($v_descr3,$name0);
        $v_descr4 = strtoupper('@'.$myrowct["adt_last"]);
        $v_descr4sp = strpos($v_descr4,$name0);
        $v_descr5 = strtoupper('@'.$myrowct["adt_email"]);
        $v_descr5sp = strpos($v_descr5,$name0);
        $v_descr6 = strtoupper('@'.$myrowct["adt_url"]);
        $v_descr6sp = strpos($v_descr6,$name0);

        if ( $v_descr3sp > 0 || $v_descr4sp > 0 || $v_descr5sp > 0 || $v_descr6sp > 0) {

        $v_adr_id10 = $myrowct["adt_adr_id"];

        if ( $myrowct["adt_adr_id"] != 0 ) {

        $resultad2 = mysql_query ("SELECT * FROM address WHERE adr_id=$v_adr_id10",$db);

	while ($myrowad2 = mysql_fetch_array($resultad2)) {

        $v_name_1_2 = $myrowad2["adr_name1"].' '.$myrowad2["adr_name2"]; }

        $search_10 = strtoupper($v_name_1_2);
        if (strpos($search_10,' ')>0) {$search_10 = substr($search_10,0,strpos($search_10,' '));}
        if (strpos($search_10,',')>0) {$search_10 = substr($search_10,0,(strpos($search_10,' ')-1));}
        if (strlen($search_10) > 10) {$search_10 = substr($search_10,0,10);}
        $v_search_10 = $search_10; 

        printf("<tr><td><font size=1>%s</font><td><a href=\"address.php?search_10=$v_search_10\"><font size=2>%s</font></a><td>.</font>\n", $myrowct["adt_adr_id"], $v_name_1_2);

        //printf("<tr><td><font size=1>%s</font><td><font size=2>%s %s %s %s</font><td><font size=2>%s %s %s %s</font>\n", $myrowct["adt_adr_id"], $v_descr3sp, $v_descr3, $v_descr4sp, $v_descr4, $v_descr5sp, $v_descr5, $v_descr6sp, $v_descr6);

        } else {

        $v_name_1_2_3 = $myrowct["adt_first"].' '.$myrowct["adt_last"];

        printf("<tr><td><font size=1>%s</font><td><a href=\"contact.php?\"><font size=2>%s</font></a><td><font size=2>%s %s</font>\n", $myrowct["adt_adr_id"], $v_name_1_2_3, $myrowct["adt_email"], $myrowct["adt_url"]);

        }

        } 

        }

echo "</table>";

echo "</table><br><br><a href=\"address.php\"><font size=4>New Address</font></a>";

}

  ?>

 </body>



Ici je filtre 40 000 enregistrements du fichier adress et 50 000 enregistrements du fichier adtel et c'est extremement long, j'ai aussi utilisé les USE INDEX mais ce n'est pas plus performant.

Le fichier address contient l'adresse géographique et le nom de la société, le fichier adtel contient tous les contacts, (maintenant il ne contient plus les téléphones), donc une infinité de contacts par adresse.

De plus le fichier adtelfax contient tous les numéros de téléphone et fax, avec une infinité de téléphones par contact. Dans adtelfax j'ai beaucoup amélioré la rapidité en comprimant le numéro de téléphone systèmatiquement un champ du genre: 01 45 23 34 54 devient 0145233454, ou encore en Amérique du Nord (622) 754-5678 devient 6227545678. Donc un strpos est très rapide.

Par contre dans le script précédent le temps de recherche est très long, et assez souvent il y a un dépassement de temps qui montre un message d'erreur.

Merci encore pour votre aide.

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

13 avr. 2006, 09:54

J'avoue : j'ai survolé ton post

Il faudrait mieux expliquer, je pense que personne n'a envie de décrypter ton code
Tu nous explique ce que contiennent tes tables, mais qu'est-ce que tu veux récupérer comme données et que veux-tu en faire ?

En effet on constate que tu fais beaucoup de traitement en PHP, et que tes requêtes sont de simples SELECT *, ça ne parait pas étonnant que ce soit lent si tu as récupères beaucoup d'enregistrement.

Mais il faudrait que tu expliques mieux ce que tu souhaites faire pour obtenir de ssuggestions.

Si tu laisses ton message en l'état j'ai peur que tu n'ais jamais aucune réponse

Un message qui explique le problème en français avec "Voici le contexte, ce que je voudrais et ce que j'ai actuellement" est bien plus compréhensible pour ceux qui voient ça de l'extérieur ;)

Eléphant du PHP | 64 Messages

14 avr. 2006, 21:31

Merci à Oucklileou et aux autres volontaires pour leur aide,

But des requêtes:
Trouver une chaîne donnée (le nom d'une personne ou d'une entreprise) dans un fichier adresse et un fichier contact

Mon problème:
Le temps de recherche est très long, et assez souvent il y a un dépassement de temps qui montre un message d'erreur.

J'utilise des requêtes SELECT * FROM, j'ai essayé INNER JOIN et USE INDEX et cela ralenti encore plus, voire bloque complètement (pour le code voir mon message précédent).

Voici les deux fichiers:

address 40 000 enregistrements, server sous MySQL 4.0.15:
adr_id int(6) auto_increment Primary Index Unique
adr_name1 varchar(40)
adr_name2 varchar(40)
adr_ctry char(3)
adr_state char(3)
adr_zip varchar(15)
adr_zip0 varchar(15)
adr_city varchar(25)
adr_stno varchar(7)
adr_styp char(3)
adr_sname varchar(30)
adr_suite varchar(10)
adr_pob varchar(20)
adr_flag char(1)
adr_mother int(6)
adr_lastchg date
adr_kd_nr varchar(5)
adr_ori int(6)

adtel 50 000 enregistrements, server sous MySQL 4.0.15:
adt_id int(6) auto_increment Primary Index Unique
adt_adr_id int(6)
adt_pos int(2)
adt_lg char(1)
adt_mr char(2)
adt_first varchar(25)
adt_last varchar(25)
adt_phone varchar(25)
adt_cell varchar(25)
adt_1800 varchar(25)
adt_fax varchar(20)
adt_func varchar(30)
adt_func0 varchar(20)
adt_email varchar(50)
adt_url varchar(100)
adt_pob varchar(20)
adt_mother_kd varchar(5)
adt_mother int(6)
adt_palm char(1)
adt_recall char(1)
adt_dtact date
adt_tact time
adt_kd_nr varchar(5)
adr_ori_tel int(6)
adt_send char(1)
adt_cat varchar(5)
adt_flag char(1)
adt_updated date

Le fichier address contient l'adresse géographique et le nom de la société, le fichier adtel contient tous les contacts, (maintenant il ne contient plus les téléphones), avec une infinité de contacts par adresse.

De plus le fichier adtelfax contient tous les numéros de téléphone et fax, avec une infinité de téléphones par contact. Dans adtelfax j'ai beaucoup amélioré la rapidité en comprimant le numéro de téléphone systèmatiquement un champ du genre: 01 45 23 34 54 devient 0145233454, ou encore (622) 754-5678 devient 6227545678. Dans adtelfax un strpos est très rapide, alors que dans les deux autres fichiers le même strpos prend une eternité, voire même mène à une erreur d'affichage (temps supérieur à 30 secondes..).

Si j'ai bien interprété le début de réaction de Oucklileou, SELECT * FROM pourrait être remplacé par une autre requête, cela m'intéresse beaucoup.

Merci d'avance pour votre aide.

ViPHP
ViPHP | 656 Messages

15 avr. 2006, 18:39

A mon avis déja ça : SELECT * FROM adtel c'est pas une bonne idée.
Tu devrais récupéré UNIQUEMENT ce dont ta besoin et pas * (tout). Même si tu ne connais par exemple que le nom d'une personne, il est inutile de le recuperer.

Ensuite utilise au maximum la clause WHERE pour une fois de plus récuperer seulement ce dont ta besoin, enfin, si tu fait plusieurs SELECT à la suite, condense les en un seul gros SELECT (même si les informations ne se trouvent pas dans la même table).

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

15 avr. 2006, 19:08

Si tu veux chercher le nom d'une personne ou d'une entreprise, tu peux faire ça directement dans ta requête

Sans lister tout, il suffit de savoir sur quelles colonnes tu veux effectuer ta recherche, et comment sont reliées tes 2 tables

Tu pourras ensuite utiliser dans la clause WHERE de quoi filtrer tes résultats et ne prendre que ce qui t'intéresse :
avec LIKE ou une recherche plein texte

Car comme l'a dit Ultiny, si tu récupères des milliers d'enregistrements pour les traiter 1 par 1 en PHP, c'est forcément plus long puisque tu récupères des tas d'enregistrements qui te sont inutiles, et MySQL est souvent plus rapide pour faire des tris suivants des critères.

ViPHP
ViPHP | 656 Messages

15 avr. 2006, 19:19

Aussi j'ajoute pour information qu'au dela d'un certain nombre d'enregistrement (et dans ton cas avec des requêtes et un serveur pas optimisé) MySQL n'est plus efficace et qu'il vaut mieu passer à PostgreSQL (qui il me semble est un poil plus rapide) ou carrement pour un prix très contrasté avec ceux des logiciels libres (:p) utiliser Oracle ou SQL Server (extrémement performant dans sa version 2005 et de plus en 64bit... j'adors) qui eux sont fait pour gerer de grosses bases (mais pas à moindre coût).

Il faut voir aussi avec l'engine que tu utilise mais InnoDB est sans doute plus puissant également, et en fonction de la RAM disponible, la charger a fond permet aussi d'avoir de meilleurs performances.

J'ai déja travaillé sur un site qui gerais entre 1 000 000 et 1 500 000 enregistrements (forum IPB, toutes tables confondus) et qui mangeait les 2Go de RAM d'un coup. Il à fallut changer de serveur pour un 4Go, donc mefit toi aussi de la saturation de ton serveur et de la bande passante.

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

15 avr. 2006, 19:46

Aussi j'ajoute pour information qu'au dela d'un certain nombre d'enregistrement (et dans ton cas avec des requêtes et un serveur pas optimisé) MySQL n'est plus efficace et qu'il vaut mieu passer à PostgreSQL
Comme tu le dis, avant de prendre en compte le nombre de lignes, le problème ici est dans la ou les requêtes.

Parceque 50 000 enregistrements, ce n'est pas encore "une grosse base".
Sur mon portable, je récupère facilement 50000 lignes d'une table MySQL en local.

Il y a le grand débat MySQL et PostgreSQL, je n'ai fait que lire des choses dessus et je n'ai pas le moyen d'avancer mes propres arguments et constatations, mais je pense pouvoir dire quand même que y'a du chemin avant de dire que MySQL ne fait pas l'affaire.
D'ailleurs les critiques concernent plutôt souvent les problèmes avec des connexions simultanées, et non la quantité de données contenues.

Donc dans ce cas ci rien que le fait de ne plus se ramener 50 000 lignes, les traiter en PHP pour n'en utiliser qu'une résoudra tous les problèmes :lol:

Eléphant du PHP | 64 Messages

15 avr. 2006, 21:26

Merci à ouckileou, à Ultiny et à tous ceux et celles qui ont réfléchi sur mon problème un samedi de Pâques,

J'apprends que je peux utiliser un SELECT pour plusieurs fichiers à la fois, alors que je me cassais la tête avec des INNER JOIN. J'ai cherché dans le guide MySql et je n'ai pas trouvé, suis-je sur la bonne piste avec quelque chose comme:

SELECT adt_first,adt_last,adt_url,adr_name1,adr_name2 FROM adtel,address WHERE ( adt_first=$chain || adt_last=$chain || adt_url=$chain || adr_name1=$chain || adr_name2=$chain);

devrais-je plutot utiliser HAVING?

J'avais déjà tenté d'utiliser WHERE auparavent mais j'obtenais un message d'erreur, ce qui m'amenait à ouvrir des SELECT les uns dans les autres.

Je suis surpris car personne ne parle de INDEX.

Je ne sais pas quoi penser sur le type de base de données

Joyeuses Pâques à tous.

Eléphant du PHP | 64 Messages

15 avr. 2006, 21:51

Ma recherche n'est pas adr_name1 = $chain

Mais plutôt un strpos après avoir fait un strtoupper, car je recherche ma chaine dans une partie des champs concernés, et dans certains cas exceptionnels $chain peut être identique au champ.

Merci encore pour vos conseils.

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

15 avr. 2006, 22:15

les INDEX servent à la recherche FULLTEXT par exemple

en fait, il faudrait que tu résumes ton problème, car je trouve que c'est fouilli :

sur quelles colonnes (ou champs c'est pareil) doivent porter ta recherche
à quoi ressemble l'expression recherchée : est-ce que tu as le nom exact ? est-ce que tu n'as que le début ?

De plus, tu parles de fichiers, mais dans une base de données ce sont de stables

Donc avant de te lancer dans le SQL, exprime clairement ton problème :
"qu'est-ce que je cherche, où je le cherche, quels éléments j'ai pour faire le tri"

Car tu reviens sur tes strpos et strtoupper mais ça c'est du PHP et c'est ça qui rend ta recherche très longue.
Nous pensons que ta recherche doit pouvoir se faire en entier dans MySQL, qui ne te renverre que les enregistrements qui correspondent à tes critères

Eléphant du PHP | 64 Messages

15 avr. 2006, 22:52

Merci ouckileou,

Voici l'information:

Recherche d'une chaine dans une partie des colonnes suivantes dans les tables suivantes

address.adr_name1
address.adr_name2
adtel.adt_first
adtel.adt_last
adtel.adt_url

SELECT adtel.adt_first,adtel.adt_last,adtel.adt_url,address.adr_name1,address.adr_name2 FROM adtel,address WHERE ( adt_first LIKE $chain || adt_last LIKE $chain || adt_url LIKE $chain || adr_name1 LIKE $chain || adr_name2 LIKE $chain);

Est-ce que LIKE va faire une recherche de la chaine en m'importe quel endroit de la colonne, ou bien ne peut partir que de la première position de la colonne?

Merci d'avance pour donner ton opinion

Mammouth du PHP | 19672 Messages

15 avr. 2006, 23:01

Attention à un détail : l'index FULL TEXT ne fonctionne que sur des tables MyISAM, pas sur des tables InnoDB ;)
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 64 Messages

15 avr. 2006, 23:26

Merci CYrano,

Toiutes mes tables sont ISAM

ViPHP
ViPHP | 656 Messages

15 avr. 2006, 23:28

Par exemple : (je sais pas si c'est bon, c'est de mémoire)

Code : Tout sélectionner

SELECT a.adt_first, a.adt_last, a.adt_url, b.adr_name1, b.adr_name2 FROM adtel as a, address as b WHERE ( a.adt_first LIKE $chain || a.adt_last LIKE $chain || a.adt_url LIKE $chain || b.adr_name1 LIKE $chain || b.adr_name2 LIKE $chain);

Eléphant du PHP | 64 Messages

15 avr. 2006, 23:41

Merci Ultiny,

Je vais essayer cette requête.

Merci à tous ceux qui m'ont apporté de nombreux éléments utiles.

Joyeuses Pâques