Problème avec LIMIT et GROUP-CONCAT dans une requete preparée

Résolu/Fermé
christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020 - 5 janv. 2018 à 11:55
jordane45 Messages postés 38347 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 24 décembre 2024 - 7 janv. 2018 à 15:21
Bonjour à toutes et à tous et meilleurs voeux pour 2018,

Je voudrais regrouper sur une seule ligne les 20 premiers résultat d'une colonne, puis les 20 suivants, etc. Pour cela, j'ai écrit le code suivant:

$i=0;
$i20=20;
$Adresses = $bdd->prepare('SELECT Courriel, agree, GROUP_CONCAT(Courriel SEPARATOR "; ") as mail FROM ce GROUP BY "Agree" LIMIT :debut, :fin');
$Adresses->bindValue(':debut',$i, PDO::PARAM_INT);
$Adresses->bindValue(':fin',$i20, PDO::PARAM_INT);
$Adresses->execute();

While($Contacts=$Adresses->fetch())
{
echo $Contacts['mail'];
$i+20;
}

Le regroupement des adresses mail se fait bien, mais la limitation à 20 lignes n'est pas prise en compte.
Si je fais la requete sans la fonction GROUP_CONCAT, la limitation fonctionne mais bien sur il n'y a pas de regrouppement sur une seule ligne.
De même, je n'ai qu'un bloc de 20 adresses et la boucle s'arrête au lieu de m'afficher tous les blocs de 20 lignes.


Des idées?
Merci par avance
Christian

4 réponses

jordane45 Messages postés 38347 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 24 décembre 2024 4 719
Modifié le 5 janv. 2018 à 12:31
Bonjour,

Pourquoi vouloir faire X requêtes ... alors que tu peux en faire une seule qui te donne tout et tu gères l'affichage via tes boucles php...


$sql = 'SELECT  agree, 
           GROUP_CONCAT(Courriel SEPARATOR "; ") as mail 
           FROM ce 
           GROUP BY Agree ';
try{
   $Adresses = $bdd->prepare($sql);
   $Adresses->execute();
   $Contacts=$Adresses->fetchAll();
}catch(Exception $e){
  echo "Erreur ! " .$e->getMessage();
}

//parcours du résultat de la requete
for ($i=0;$i<20;$i++){
  echo $Contacts[$i]['mail'];
}


// second parcours du résultat de la requete
for ($i=20;$i<40;$i++){
  echo $Contacts[$i]['mail'];
}




A moins que ta question c'est d'avoir 20 adresses PAR "Agree"...
Là... il faut modifier la requête (en utilisant les limit )

Un truc du genre
SELECT T.agree, 
               GROUP_CONCAT(T.Courriel SEPARATOR "; ") as mail
FROM(
   SELECT  agree, Courriel 
   FROM ce 
   LIMIT :debut, :fin
 ) T
 GROUP BY T.Agree


Cordialement, 
Jordane                                                                 
0
christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020
5 janv. 2018 à 14:40
Merci Jordane de ta réponse.
La question est d'avoir les adresses courriel des Agree par groupe de 20 regroupé sur une seule ligne, afin d'envoyer des mssages aux adhérents d'une association (au nombre de 260) en s'affranchissant des contraintes de l'hébergeur en nombre de message par eure et nombre de destinataire par message.
Le problème n'est pas résolu par ta proposition: affichage de l'ensemble des courriels (avec la limitation à 1024 bits propre à GROUP_CONTAT) et affichage du message d'erreur :
( ! ) Notice: Undefined offset: 1 in C:\wamp64\www\xxxxxxx\Mail\EnvoiMessage.php on line 16.
(J'ai traduit le numéro de ligne par rapport à ta proposition de code et ixé le nom du site).
0
jordane45 Messages postés 38347 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 24 décembre 2024 4 719 > christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020
5 janv. 2018 à 15:10
Il faudrait que tu fasses un
SET @@group_concat_max_len = 2048;

Avant ta requête

Mais de toutes façons... vu ta demande ... ça serait plus ma seconde réponse qui conviendrait
0
christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020
5 janv. 2018 à 15:04
Oh, j'ai oublié de préciser que le message s'affiche 20 fois avec offset 1, puis 2, puis 3,...
On a donc d'abord l'affichage de l'ensemble des courriels, puis ensuite 20 fois le message d'erreur.
0
christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020
5 janv. 2018 à 23:00
bonsoir,
La solution au problème avance: maintenant les 20 1ère lignes sont bien affichées mais la boucle s'arrête et elle n'affiche pas les blocs suivants de 20 entrées. Voici le code :
        $debut=0;
	$fin=20;

	$Adresses = $bdd->prepare('SELECT T.Agree, GROUP_CONCAT(T.Courriel SEPARATOR "; ") as mail
	FROM (
	SELECT Agree, Courriel FROM ce LIMIT :debut, :fin) T GROUP BY T.Agree');
	      $Adresses->bindValue(':debut',$debut, PDO::PARAM_INT);
              $Adresses->bindValue(':fin',$fin, PDO::PARAM_INT);
              $Adresses->execute();

While($Contacts=$Adresses->fetch())
	{
	echo $Contacts['mail'];
	$debut++;
 	}				
0
jordane45 Messages postés 38347 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 24 décembre 2024 4 719 > christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020
5 janv. 2018 à 23:31
A quel moment relances tu la requête en incrémentant les variables de début et de fin ?

Au passage... tu as retiré le bloc try/catch que je t'avais ajouté...
Je t'invite à appliquer ceci : https://forums.commentcamarche.net/forum/affich-37584941-php-pdo-gerer-les-erreurs
0
Hou que c'est moche votre façon de faire. Normal quand on débute et qu'on Ces 3 ensembles distincts(le plus possible) se nomment architecture 3 tiers(ou tripartie).
L'intérêt est que le http(le web quoi) peut accéder à la base de données indirectement(via PHP) mais que la base de données elle même n'est jamais accessible directement par Internet. Cela garantit la sécurité de celle ci avec un minimum de règles de PHP.

Concrètement une base de données possède un langage rudimentaire de programmation, langage de requêtes nommé S.Q.L. => Structured Query Language.

Donc quand vous parlez de requêtes il ne s'agit pas de PHP mais de SQL.
SQL donc 1)chacun et son rôle et attribuez les rôles.
2) Si la question concerne une requête(obtention, modification, tri, classement des données de la base) la réponse est SQL. Si la réponse concerne l'affichage ou la logique du programme ainsi que l'obtention des données d'un formulaire la réponse est PHP. Ce qui implique la plupart du temps la fonction PHP echo a pour effet d'afficher dans le HTML.
La passerelle n'est pas imagée elle est bien concrète: de la page web vers la base et dans l'autre sens.

Sans vouloir rentrer dans le détail pour expliquer tout les problèmes votre structure qui mélange du PHP et du SQL pour un même qui est du pur SQL c'est un non sens et une erreur de conception. Je citerais toutefois l'inutile complexité du code, la perte de temps d’exécution de 2 serveurs qui n'ont rien en commun(Apache et le serveur de la base) mais bon il en existe d'autres qui sont assez triviaux.

Bref comme le laisse suggérer l'intitulé de la question la réponse étant SQL(la fonction LIMIT) est simplement ici:
http://sql.sh/cours/limit

Notez qu'une technique de débug simple est de vérifier ses requêtes directement sur le serveur SQL(ex: avec WAMP/easyPHP ou par la console). Vous verrez vite: La requête donne le bon résultat directement sur la base l'erreur vient du côté PHP ou si la requête ne donne pas le résultat escompté c'est la requête qui est à refaire.
0
jordane45 Messages postés 38347 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 24 décembre 2024 4 719
6 janv. 2018 à 00:10
Je me demande si un code de ce genre ne ferait pas l'affaire...

<?php


// Récupération de toutes les adresses
function getAllContacts(){
  global $bdd;
  $sql = 'SELECT  agree, Courriel
          FROM ce 
          ORDER BY Agree ';          
  try{
     $prepare = $bdd->prepare($sql);
     $prepare->execute();
     $result = $Adresses->fetchAll();
  }catch(Exception $e){
    echo "Erreur ! " .$e->getMessage();
  }
  return !empty($result) ? $result : NULL ;
}

//Fonctions pour réorganiser l'array des mails
function reorderListMails($Contacts=array()){
  $arr_contacts = array();
  if(!empty($Contacts)){
    //on boucle sur l'array des Contacts 
    foreach($Contacts as $C){
      $arr_contacts[$C['agree']][] = $C['Courriel'];
    } 
  }
 return !empty($arr_contacts) ? $arr_contacts : NULL;
}

//Fonctions pour générer les blocs de 20 adresses par agree
function getBlocsListMails($a_contacts=array()){
  foreach($a_contacts as $agree => $mails){
    echo "<pre>".$agree."<br>";
    $a_adressesMails = getGroupeListeMails($mails,0,19);
    
    if(!empty($a_adressesMails)){
      foreach($a_adressesMails as $k=>$emails){
         echo "<br>".$emails;      
      }
    }
     echo "</pre>";
  }
}

//génère des liste de 20 mails
function getGroupeListeMails($a_mails = array() , $start = 0,$end = 19 ){
    $a_listMails = array();
    $strMails = array();
    //on vérifie que la variable end est inférieur ou égale au nombre max de mails présents dans l'array
    $end = $end <= count($a_mails) ? $end : count($a_mails);
    
    //on génère le bloc
    for($i=$start;$i<=$end;$i++){
         $a_listMails[] = $a_mails[$i];
    }
    $strMails[] = array(join(",",$a_listMails));
    
    if($end <= count($a_mails) ){
      //si il reste des adresses.. on passe au 20 suivantes
      $strMails[] = getGroupeListeMails($a_mails,$end+1,$end + 20);
    }else{      
      return $strMails;   
    }     
}

//------------------------------------------------//
// Début du script :
//------------------------------------------------//
$Contacts = getAllContacts();

//le temps des tests :
print_r($Contacts);

$arr_mails = reorderListMails($Contacts);

//le temps des tests :
print_r($arr_mails);



getBlocsListMails($arr_mails);






NB: Je l'ai codé de tête... il peut y avoir des erreurs qu'il faudra corriger.....
0
christian82000 Messages postés 47 Date d'inscription dimanche 27 décembre 2009 Statut Membre Dernière intervention 17 septembre 2020
7 janv. 2018 à 15:16
Bonjour à tous,
Pour Veltur,
J'ai bien compris les conseils que tu m'a donné. Il me semble que je n'étais pas si loin de tes conceptions architecturales : SQL d'une part et PHP d'autre part. Mon problème provenait bien initialement de la conception de la requête SQL, l'imbrication des 2 ne se faisant pas dans le bon ordre. Ensuite, et seulement ensuite, il y a eu un problème d'affichage PHP...
J'avais parcouru le site : https://sql.sh/cours/limit mais je n'y avais pas trouvé la réponse à ma question : comment faire cohabiter les fonctions GROUP_CONCAT() et LIMIT() dans une requête préparée, toutes deux relevant du SQL (seulement MySQL pour GROUP_CONCAT). Pas plus que sur d'autres sites ou forum que j'avais parcouru. Me voila donc sur le forum MySQL de Comment ça marche! Mais le sujet a été déplacé depuis sur le forum PHP.
En tout cas, merci pour t'être intéressé à mon problème.

Pour Jordane.
Tu me propose une solution intéressante avec 3 fonctions. Testée, il y avait au moins 2 bugs (mais tu avais prévenu) et qui, après débogage, doit fonctionner. Mais en fait, mon script est plus complexe que celui que j'ai publié, car il y a 4 alternatives possibles, avec 2 variables présentes ou pas, seules ou conjointes... Ces variables font l'objet de "where" présent ou pas. Mais ce problème est résolu. En fait, je n'ai pas su adapter tes fonctions à cette problématique.
Je me suis donc contenté d'adapter la 2è proposition de ton premier message, et là, tout fonctionne correctement.
Je n'ai pas très bien compris le "T" présent dans le premier SELECT mais apparemment, c'est lui qui fait fonctionner l'ensemble des 2 requêtes imbriquées. J'ai eu beau vérifier sur les cours en ligne ou sur les forums, je n'ai pas trouvé l'explication.
Donc, un grand merci pour t'être penchée sur mon problème et m'avoir conduit vers une solution fonctionnelle.
Je mets le sujet en résolu.
Pour ceux qui seraient intéressé par le code adopté, le voici :
<?php

$debut=0;
$fin=20;
while($debut<300)
{
$Adresses = $bdd->prepare('SELECT T.Agree, GROUP_CONCAT(T.Courriel SEPARATOR "; ") as mail
	FROM (
       SELECT Agree, Courriel FROM ce LIMIT :debut, :fin) T GROUP BY T.Agree');
	$Adresses->bindValue(':debut',$debut, PDO::PARAM_INT);
	$Adresses->bindValue(':fin',$fin, PDO::PARAM_INT);
        $Adresses->execute();

$Contacts = $Adresses->fetch();
echo $dest=$Contacts['mail'];
if($dest == null)	
	{
	exit();
	}			
$debut+=20
}

?>


PS : Le tri{} casch{} est contenu dans le script de connexion à la base. Est-il vraiment utile de le reprendre dans la requête SQL?

Bien cordialement
Christian
0
jordane45 Messages postés 38347 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 24 décembre 2024 4 719
7 janv. 2018 à 15:21

Je n'ai pas très bien compris le "T" présent dans le premier SELECT mais apparemment, c'est lui qui fait fonctionner l'ensemble des 2 requêtes imbriquées. J'ai eu beau vérifier sur les cours en ligne ou sur les forums, je n'ai pas trouvé l'explication

C'est ce qu'on appel un ALIAS
Au même titre que tu peux mettre des ALIAS à tes champs dans ta requête (en faisant un AS )... tu peux (dois) mettre des ALIAS sur tes tables (surtout quand tu utilises des sous-requêtes comme c'est le cas ici )


PS : Le tri{} casch{} est contenu dans le script de connexion à la base. Est-il vraiment utile de le reprendre dans la requête SQL?

Alors ce n'est pas tri et casch .. mais TRY et CATCH
Ensuite... celui de la connexion... sert ... pour la connexion.

Ensuite.. tu dois en avoir un pour CHAQUE requête. C'est grâce à ça que tu peux "attraper" / intercepter (catch ) les éventuelles erreurs.
0