Jump to content

Réduire la requête


Recommended Posts

Bonjour à tous,

Pour un de mes scripts je dois faire afficher des données en fonction du temps. Sauf que je fais plusieurs requêtes à la suite et j'ai peur qu'a la longue, le serveur souffre un peu si beaucoup de gens l'utilisent en même temps. Voici mes requêtes :

$start = $e['date_envoi_emailing'];            $h1 = 1000 * 60 * 60;            $end = $start + $h1;            $query = "";            $tab = array();            for($i = 0; $i < 24; $i++) {                if($i < 23) {                    $query = "SELECT SUM(tracking.if_ouvert_tracking) as hPlus".$i."                            FROM tracking                            WHERE id_emailing = '".$id_emailing."'                                AND id_user = '".$_SESSION['user']['id']."'                                AND url_tracking = 'ouverture'                                AND date_tracking >= $start AND date_tracking <= $end; ";                    $start = $start + $h1;                    $end = $start + $h1;                } else {                    $query = "SELECT SUM(tracking.if_ouvert_tracking) as hPlus".$i."                            FROM tracking                            WHERE id_emailing = '".$id_emailing."'                                AND id_user = '".$_SESSION['user']['id']."'                                AND url_tracking = 'ouverture'                                AND date_tracking >= $start;";                }                $req = $db->prepare($query);                $req->execute();                $r = $req->fetch();                if(!isset($r['hPlus'.$i])) {                    $r['hPlus'.$i] = 0;                }                $tab[$i] = intval($r['hPlus'.$i]);            }

Comme vous pouvez le voir c'est long. Je vais 23 requêtes et j'ai encore le même type de requête pour déterminer le nombre de clics dans le temps.

Comment je peux faire ?

Merci d'avance

Link to comment
Share on other sites

EDIT 3 : où je me rends compte que j'ai mal lu les requêtes (l'ayant fait en speed). Ce message est à ignorer, passer directement au suivant

Si je comprends bien, tu as 24 tables correspondant à autant de tranches horaires , j'imagine avec des scripts décalant les données de table en table toutes les heures ? Si tel est bien le cas, je dirais que tu as un gros problème de design dans ta base de donnée. Ce n'est pas la table dans laquelle l'information est présente qui devrait déterminer son "âge", vu que ça te complique inutilement (et que ça t'empêche de faire des clefs étrangères vers celles-ci, même si j'imagine que tu n'en as dans la situation présente pas l'utilité).

Si tu n'as pas la possibilité de modifier le schéma de la base de données, c'est mort, tu es obligé de fonctionner tel quel. Une éventuelle optimisation serait de construire une grosse requête à coup d'UNION et de tout soumettre d'un coup au gestionnaire de base de données mais je doute que ça puisse améliorer quoi que ce soit...

Si tu peux modifier le schéma de base de données, fait une seule table H avec les mêmes champs que tes tables hPlus* auquel tu ajoutes une colonne elapsed_hours que tu initialises à 0. À ce moment-là, ton script de maintenance pourra se contenter de faire un simple UPDATE en incrémentant ce compteur pour tous les enregistrements, puis un DELETE pour ceux qui sont devenu trop vieux.

EDIT : J'oubliais, si les hPlus* sont des vues, alorsil suffit d'utiliser la ou les tables sous-jacentes dans le cas présent.

EDIT 2 : La solution que je propose ci-dessus est sub-optimale. Il y a moyen de s'en sortir sans avoir à faire d'UPDATE régulier en utilisant une colonne de type DATETIME plutôt qu'elapsed_hours proposée plus haut.

Link to comment
Share on other sites

Le code suivant devrait marcher si tes plages horaires sont à heure "rondes" (je ne sais pas comment dire), c'est à dire 5h00, 6h00 etc...

select sum(t1.ouverture), t1.plage_horaire
from (
SELECT tracking.if_ouvert_tracking as ouverture, DATE_FORMAT(date_tracking, '%H') as plage_horaire
                            FROM tracking
                            WHERE id_emailing = '".$id_emailing."'
                                AND id_user = '".$_SESSION['user']['id']."'
                                AND url_tracking = 'ouverture'
                                AND date_tracking >= $start ) as t1
group by plage_horaire;

La sous requête ramène tous les éléments de tes 24 requêtes précédente en une fois.

Le bout

DATE_FORMAT(date_tracking, '%H') as plage_horaire

extrait l'heure de la date (de 00 à 23).

Lla requête "externe" fait la somme par plage horaire.

Si tes plages horaires ne sont pas à heure rondes, la diffculté sera de remplacer DATE_FORMAT(date_tracking, '%H') par un bout de code qui construirais des plages horaires à partir de l'heure de départ, ce qui est assez tordu mais peut être faisable.

Link to comment
Share on other sites

Je me rends compte que j'ai oublié de données quelques infos. Les plages horaires ne sont pas stockés. En fait c'est à partir du moment où la personne à lancé la fonction d'envoi du mail que les palges horaires sont calculés et le tout est stocké en timestamp.

C'est à dire que si la personne lance l'email avec ce timestamp : 1389087387, je fais à chaque fois +3600 pour aller 1h en plus et je m'arrête au delà de 23h après l'envoi.

Link to comment
Share on other sites

Arf, Je me doutais bien que tu partais d'une heure quelconque (d'où ma dernière phrase), mais j'ai pensé à quelque chose cete nuit.

Tu connais ta date de départ, donc tu peux la soustraire de ton timestamp (c'ets facile puisque tu manipule des timestamp) et faire une division euclidienne par 3600.

En supposant que $start soit bien le timestamp du début de ta plage horaire, ca donnerait ça :

select sum(t1.ouverture), t1.plage_horairefrom (SELECT tracking.if_ouvert_tracking as ouverture, ( ( date_tracking - ".$start." ) DIV 3600 ) as plage_horaire                            FROM tracking                            WHERE id_emailing = '".$id_emailing."'                                AND id_user = '".$_SESSION['user']['id']."'                                AND url_tracking = 'ouverture'                                AND date_tracking >= $start ) as t1group by plage_horaire;

PS: je connais pas le php, ma syntaxe n'est peut être pas correcte. De plus, je n'ai pas tester la solution de la division (j'ai pas de BDD avec un timestamp sous la main), je te laisse essayer.

Link to comment
Share on other sites

J'ai changé un peu la requête et ça fonction :

$query = "SELECT ";

for($i = 0; $i < 24; $i++) {
                if($i < 23) {
                    $query.= "(SELECT SUM(if_ouvert_tracking)
                            FROM tracking
                            WHERE id_emailing = '".$id_emailing."'
                                AND id_user = '".$_SESSION['user']['id']."'
                                AND url_tracking = 'ouverture'
                                AND date_tracking >= ".$start." AND date_tracking <= ".$end.") as hPlus".$i.", ";
                    $start = $start + 3600;
                    $end = $start + 3600;
                } else {
                    $query.= "(SELECT SUM(if_ouvert_tracking)
                            FROM tracking
                            WHERE id_emailing = '".$id_emailing."'
                                AND id_user = '".$_SESSION['user']['id']."'
                                AND url_tracking = 'ouverture'
                                AND date_tracking >= ".$start.") as hPlus".$i;
                }

                if(!isset($r['hPlus'.$i])) {
                    $r['hPlus'.$i] = 0;
                }

                $tabOuverture[$i] = intval($r['hPlus'.$i]);
            }

Et du coup c'est nettement plus simple et rapide. J'ai les données que je veux

Link to comment
Share on other sites

Euuh, je comprends pas pourquoi c'est plus rapide...
Tu dois toujours faire 24 requêtes, c'est pas "génial".
Tandis que la solution de fragzepika te retourne toutes tes données en une requête (à moins que je me plante ?).

Étonnant.

Link to comment
Share on other sites

Ce qui doit accélérer dans sa requête, c'est qu'il fait les 24 requêtes en une fois (une requête avec 24 sous-requêtes), au niveau de la BDD, ce ne doit pas être beaucoup plus rapide, mais il gagne le temps de communication entre son serveur php et la base de données.

Sinon, je pense effectivement que ma requête est plus optimisée. J'aurais bien aimé le tester.

@Goghvan : en général, oui, mais ça dépend aussi du volume de données ramenées, si tu as une grosse requête avec pleins de jointures qui te ramène plein de lignes, tu auras beaucoup de déchets (informations dupliquées à cause des jointures). Il vaut mieux découper en plusieurs requêtes. J'ai déjà eu à faire avec un client qui se souciait beaucoup de la bande passante et qui m'a fait découper ma belle requête qui ramène tout en plusieurs requêtes.

Link to comment
Share on other sites

Ce qui doit accélérer dans sa requête, c'est qu'il fait les 24 requêtes en une fois (une requête avec 24 sous-requêtes), au niveau de la BDD, ce ne doit pas être beaucoup plus rapide, mais il gagne le temps de communication entre son serveur php et la base de données.

Sinon, je pense effectivement que ma requête est plus optimisée. J'aurais bien aimé le tester.

Ah effectivement ! Ma faute... Je n'ai pas vu que les 24 requêtes ont été transformées en 1 requête composée de 24 sous-requêtes...

Sans méchanceté aucune envers babeuloula, j'ai l'impression que c'est assez "moche" comme solution...

La tienne de solution est tout de même plus harmonieuse.

De mon côté, j'ai pensé à exploiter une clause "having" pour venir "filtrer" plus précisément les résultats. Mais je crois que c'est un craquage (une incompréhension de ce que fait having...).

Link to comment
Share on other sites

Il me semble qu'having vient "affiner" ("filtrer") les résultats d'un group by. Je crois qu'il est tout à fait envisageable d'écrire une "sorte de sous-requête" dans un having (celle-ci s'applique cependant toujours au résultat du group by).

En tout cas, l'utilisation de having comme je le supposais est effectivement totalement inadpatée à la situation :chinois:

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...