Traitement fractionné d'une table de BDD (avec mysqli & php)

Signaler
Messages postés
18
Date d'inscription
jeudi 2 février 2017
Statut
Membre
Dernière intervention
7 février 2020
-
jordane45
Messages postés
27720
Date d'inscription
mercredi 22 octobre 2003
Statut
Modérateur
Dernière intervention
29 février 2020
-
Bonjour,

Confronté au traitement séquentiel d'une table imposante de BDD, je tente de pratiquer une itération par blocs de 4000 lignes.
Mon script présenté ci-dessous fonctionne parfaitement pour une table n'excédant pas 4000 lignes, mais au-delà le processus tourne indéfiniment sans aucun affichage de résultats.
Si je teste manuellement chaque boucle (ex: table de 14000 lignes) directement par phpmyadmin, tout est correct.

nb: si je place une instruction "break' entre mes 2 boucles "while" -pour cesser le processus à la fin de la 1ère boucle-, les résultats sont bons.


<?php
$conn = new mysqli('localhost','user','pass','bdd');

// décompte du nombre de lignes à traiter
$riq = "SELECT id FROM syw7g_ameliv
WHERE ((cp_ville != '') AND (a3 = 'C') AND (url IS NOT NULL) AND !(((code_profession = '60') AND (b5 = 'S'))
OR (code_profession = '86') OR (code_profession = '50') OR (code_profession = '21') OR (code_profession = '28') OR (code_profession = '98')))";
$resultat = $conn->query($riq) or die('Erreur SQL !'. $riq .'' . $conn->error);
$nbl = $resultat->num_rows;

// calcul du nombre d'itérations de 4000 lignes à traiter
$boucles = intval($nbl/4000);
$ntot = $boucles*4000;
if ($ntot < $nbl) {$boucles = $boucles + 1;}

// préparation du tableau d'affichage des résultats
echo '<table cellpadding="1" cellspacing="1" border="3" style="font-size:12px;">';
echo '<thead>';
echo '<tr class="centrer-noir">';
echo '<th class="centrer">Praticien</th>';
echo '<th class="centrer">Profession</th>';
echo '<th class="centrer">Spécialité</th>';
echo '<th class="centrer">Adresse</th>';
echo '<th class="centrer">Ville</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';

$debut = -4000;
$ajout = 4000;
$num_boucle = 1;
while($num_boucle <= $boucles) {
$debut = $debut + 4000;
if ($num_boucle == $boucles) {$ajout = $nbl-$debut;}
$resultat->free();
$riq = "SELECT nom, prenom, profession, specialite, cp_ville, num_rue, voie, nom_voie, url FROM syw7g_ameliv
WHERE ((cp_ville != '') AND (a3 = 'C') AND (url IS NOT NULL) AND !(((code_profession = '60') AND (b5 = 'S')) OR (code_profession = '86') OR (code_profession = '50')
OR (code_profession = '21') OR (code_profession = '28') OR (code_profession = '98'))) ORDER BY nom LIMIT " .$ajout. " OFFSET " .$debut;
$resultat = $conn->query("SET NAMES utf8");
$resultat = $conn->query($riq) or die('Erreur SQL !'. $riq .'' . $conn->error);

while($arr = $resultat->fetch_array(MYSQLI_NUM)) {
echo '<tr>';
$nom = $arr[1] . ' ' .$arr[0];
$profession = ucfirst(mb_strtolower($arr[2]));
$specialite = ucfirst(mb_strtolower($arr[3]));
echo "<td class='centrer'>".$nom."</a></td>";
echo "<td class='centrer'>".$profession."</td>";
echo "<td class='centrer'>".$specialite."</td>";
echo "<td class='centrer'>".trim(mb_strtolower($arr[5])). " " .trim(mb_strtolower($arr[6])). " " .trim(mb_strtolower($arr[7]))."</td>";
echo "<td class='centrer'>".trim(mb_strtolower($arr[4]))."</td>";
echo '</tr>';
}
$num_boucle++;
}
echo '</tbody>';
echo '</table>';

$resultat->free();
$conn->close();
?>


Grand merci à celui d'entre-vous qui m'indiquera mon erreur ... et son correctif !
Bonne journée à tous.


Configuration: Linux / Firefox 72.0

2 réponses

Messages postés
9766
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 février 2020
545
bonjour, il est souvent utile, dans ce genre de situation, d'ajouter des echo pour suivre ce que fait le programme: pour vérifier la valeur des variables et quelles instructions sont exécutées.
clend
Messages postés
18
Date d'inscription
jeudi 2 février 2017
Statut
Membre
Dernière intervention
7 février 2020

J'entends bien, et j'utilise souvent cette technique en développement.
Mais il se trouve -sans doute dû au dysfonctionnement de ma boucle- qu'aucun affichage n'apparaît à l'écran tant que ma table n'est pas refermée.
J'ai tout essayé : echo, printf, print_r, alert javascript. Toutes ces méthodes restent muettes !

Ceci dit, le problème se situant à l'évidence à l'intérieur de mes 2 "whiles", il y a peu de code et de variables à analyser (essentiellement les valeurs pour les paramètres "LIMIT" & "OFFSET", soit 2 valeurs). Disons 3 en tenant compte du n° d'itération.
yg_be
Messages postés
9766
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 février 2020
545 > clend
Messages postés
18
Date d'inscription
jeudi 2 février 2017
Statut
Membre
Dernière intervention
7 février 2020

as-tu utilisé flush()?
as-tu le même comportement si tu ne gardes que la structure du programme et n'interroges pas la bdd dans les boucles?
clend
Messages postés
18
Date d'inscription
jeudi 2 février 2017
Statut
Membre
Dernière intervention
7 février 2020
> yg_be
Messages postés
9766
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 février 2020

Bravo à toi !
Je connaissais bien flush(), mais ne l'utilisant plus depuis longtemps je l'avais complètement oublié.
Donc, en forçant le vidage du buffer à la fin de chaque itération, l'ensemble de ma table se vide bien et vient s'afficher dans sa globalité sélectionnée.
J'ai dû utiliser la conjugaison :
ob_flush();
flush();
Un pépin néanmoins : après ce bon déroulé, le processus se plante avec l'erreur :

--> 0 - PHP regular expression limit reached (pcre.backtrack_limit)

J'ai pourtant une valeur élevée pour ce paramètre de php : 1500 000 ! et idem pour pce.recursion_limit (en accord avec mon hébergeur).
yg_be
Messages postés
9766
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 février 2020
545 > clend
Messages postés
18
Date d'inscription
jeudi 2 février 2017
Statut
Membre
Dernière intervention
7 février 2020

le soucis est donc dans une autre partie du code?
Messages postés
27720
Date d'inscription
mercredi 22 octobre 2003
Statut
Modérateur
Dernière intervention
29 février 2020
2 152
Bonjour,

Exécuter des requêtes dans des boucles n'est clairement pas une bonne solution.
Ce que j'aimerai déjà déterminer c'est... sans le code php.. ta requête est-elle longue à afficher le contenu ?
Pour ça, commence par la tester DIRECTEMENT dans ta BDD (soit en ligne de commande, soit via l'interface de phpmyadmin )

Je te propose, au passage, une petite modification de ta requête
SELECT nom, prenom, profession, specialite, cp_ville, num_rue, voie, nom_voie, url 
FROM syw7g_ameliv
WHERE  cp_ville != '' 
   AND a3 = 'C' 
   AND (url IS NOT NULL) 
   AND ( code_profession NOT IN('86' , '50', '21', '28', '98') 
       OR (code_profession!='60' AND b5='5') 
       )
 
  ORDER BY nom 

Histoire d'accélerer un peu ta requête ( un peu beaucoup souvent....) il faudrait également voir si il y a bien des INDEX sur tes tables
A minima, je pense que tu peux en ajouter sur les champs code_profession et a3 et b5 (tu trouveras de nombreux tutos sur le net sur la mise en place d'INDEX dans les tables )

Le plus long dans ton code, ensuite, étant la "manipulation" des variables en PHP;
Par exemple, pour transformer en minuscule, il existe, en mysql, la fonction LOWER
https://www.w3resource.com/mysql/string-functions/mysql-lower-function.php
Idem pour le TRIM
https://www.w3resource.com/mysql/string-functions/mysql-trim-function.php
Et pour concaténer des champs ensembles
https://www.w3schools.com/sql/func_mysql_concat.asp

Pour avoir la première lettre en majuscule tu peux également mixer les fonction UCASE et LEFT

Bref... tout le traitement fait via ta requête SQL ne sera plus à faire en php dans des boucles... et ça ne fera que réduire le temps de traitement de ton script.


Pour finir, si tu as tant de lignes que ça à afficher, mets en place un système de pagination de façon à n'afficher qu'un millier de lignes à la fois et des boutons de changements de pages pour afficher les enregistrements suivants ou précédents...


NB: en mysqli on n'utilise plus les OR DIE ....
Fais une recherche sur le net sur le "traitement des erreurs mysqli" pour trouver des exemples plus "propres".