IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Requêtes MySQL Discussion :

Comment optimiser une Requete avec Count ?


Sujet :

Requêtes MySQL

  1. #1
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut Comment optimiser une Requete avec Count ?
    Bonjour à Tous,

    Voilà confronté à un problème de lenteur dans une application PHP-Mysql, je cherche à optimiser en premier lieu ma requête SQL.

    Contexte simplifié :
    J’ai 2 tables.
    Une table demande et une table Contact.
    Un contact peut effectuer 0 ou plusieurs demandes.
    Un contact se caractérise par de nombreux attributs dont une adresse email.
    Mes deux tables sont reliées via l’id_contact.

    Je cherche à connaître le nombre de demandes effectuées par un contact.
    En pratique, j’affiche un tableau des demandes du type :

    Date….|..Nom……|..Email……………..…..|
    ------------------------------------------------------
    3/1/05..|..Toto…….|..toto@tata.fr ….……….|
    2/12/04|..Leon…….|..l.durand@abc.fr (2)…..|
    1/12/04|..Nath…….|..noemie@top.ua …..…..|

    Ainsi donc l’utilisateur sait que le contact dont l’adresse email est l.durand@abc.fr a effectué 2 demandes.

    Pour ce faire en intégrant le code PHP, en gros, je fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    < ?
    $requete = ‘SELECT demande.id_demande, demande.date_demande, contact.nom, contact.email1 FROM demande LEFT JOIN contact ON demande.contact_id = contact.id_contact’ ;
    $resultat = ExecRequete ($requete, $connexion); // avec ExecRequete une fonction qui utilise mysql_query() et les paramètres de connexion à la base Mysql qui vont bien.
     
    $i=0;
    while($i<mysql_num_rows($resultat))
    {
    $demandeC = mysql_fetch_object ($resultat);
    $date_demande_fr = date("d-M-y", strtotime($demandeC->date_demande));
    $existance = ExecRequete('SELECT count(contact.email1 ) AS nb FROM contact JOIN demande ON demande.contact_id = contact.id_contact WHERE contact.email1 = \''.$demandeC->email1.'\'', $connexion);
     
    $nb_email = mysql_fetch_object ($existance);
    if ($nb_email->nb > '1'){
    $Nb = '('.$nb_email->nb.')';}
    else {
    $Nb ='';}
     
    echo '<TR><TD>',$date_demande_fr,
    '<TD>',$demandeC->nom ,
    '<TD>',$demandeC->email1,' ',$Nb,'</TD></TR>';
     
    $i++;
    }
     
    ?>
    Parmi les questions que je me pose, peut-on faire mieux que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT count(contact.email1 ) AS nb FROM contact JOIN demande ON demande.contact_id = contact.id_contact WHERE contact.email1 = ‘$email’
    pour calculer le nombre de demandes qu’a effectuées un contact
    L’enjeu est important car cette requête est utilisée dans une boucle.

    Mysql doit donc parcourir toute la table demande et dans le même temps pour chaque enregistrement trouvé indiquer combien de demandes ont été effectué par un même contact.


    Lorsque je fais un explain de cette requête j’ai les résultats suivant :

    id |select_type| table ..|type.. | possible_keys…....| key…….….| key_len | ref …………………....|rows…..| Extra

    1 .|SIMPLE…|demande| ALL |NULL………..…. |NULL……….| NULL ...|NULL………………….. |9991254 ...|

    1 .|SIMPLE ...|contact |eq_ref |PRIMARY,email1| PRIMARY..| 4………| X.demande.contact_id |1 ……...|Using where


    Espérant avoir exposé ma problématique le plus clairement possible, je vous remercie par avance des conseils que vous pourrez m’apporter.

    Pour info :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    CREATE TABLE `demande` (
    `id_demande` int(11) NOT NULL auto_increment,
      `contact_id` int(11) NOT NULL default '0',
    `date_demande` datetime NOT NULL default '0000-00-00 00:00:00',
      PRIMARY KEY  (`id_demande`)
    ) ENGINE=MyISAM AUTO_INCREMENT=23411 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci PACK_KEYS=0 AUTO_INCREMENT=xxxxx ;
     
    CREATE TABLE `contact` (
    `id_contact` int(11) NOT NULL auto_increment,
    `nom` varchar(100) collate latin1_general_ci NOT NULL default '',
    `email1` varchar(250) collate latin1_general_ci NOT NULL default '',
    PRIMARY KEY  (`id_contact`),
    KEY `email1` (`email1`)
    ) ENGINE=MyISAM AUTO_INCREMENT=27370 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=yyyyy ;
    Un contact est identifié par son adresse email et peut être présent plusieurs fois dans la table contact.

  2. #2
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    Le principe général est qu'il ne faut pas faire de requête dans une boucle. Une grosse requête sera toujours plus rapide que plusieurs petites...

    Pour appliquer à ton cas, je reprends ton tableau :
    Date….|..Nom……|..Email……………..…..|
    ------------------------------------------------------
    3/1/05..|..Toto…….|..toto@tata.fr ….……….|
    2/12/04|..Leon…….|..l.durand@abc.fr (2)…..|
    1/12/04|..Nath…….|..noemie@top.ua …..…..|
    Que représente Date ? si ça veut dire que Leon a fait deux demandes le 2/12/04, la requête unique est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT date_demande, nom, email1, COUNT(id_demande) AS Nb
    FROM contact C
      LEFT JOIN demande D ON D.contact_id = C.id_contact 
    GROUP BY date_demande, nom
    Sinon, précise la signification de cette colonne.

  3. #3
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    Bonjour Antoun,

    Pour répondre à ta question : date représente la date où un internaute fait une demande.
    2/12/04|..Leon…….|..l.durand@abc.fr (2)…..|
    Leon a fait au total 2 demandes, l'une le 2/12/2004 et une autre par exemple le 31/12/2000. Je ne peux donc pas faire de requête avec un group by. Le 2 représente donc le nombre total de demandes qu'a effectuées un utilisateur quelque soit les dates de ses demandes.
    L'identification se fait via l'adresse email.


    PS : je n'ai pu répondre à ta question avant suite à un incident chez mon FAI ...
    plus d'internet ...

  4. #4
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    ça donne donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT MIN(date_demande), nom, email1, COUNT(id_demande) AS Nb
    FROM contact C
      LEFT JOIN demande D ON D.contact_id = C.id_contact 
    GROUP BY nom

  5. #5
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    Merci Antoun pour ta proposition, mais elle ne donne pas le résulat que je souhaite.

    Cas concret :
    id_contact…nom…………..email1
    -------------------------------------------------------
    1………….....TOTO……….…toto@free.fr
    2………….....LEON…………..leon@wanadoo.fr
    3………….....TOTO……….…toto@free.fr
    4………….....PAUL1………...paul@neuf.fr
    5………….....NOEMIE……...nono@club.fr
    6………….....PAUL2……...…paul@neuf.fr

    id_demande….date_demande……contact_id
    ---------------------------------------------------------
    1…………….....25/01/2006……………..1
    2…………….....12/04/2006……………..2
    3…………….....13/05/2006……………..3
    4…………….....13/06/2006……………..4
    5…………….....03/09/2006……………..4
    6…………….....04/09/2006……………..4
    7…………...…..02/01/2007……………..6


    Exemple concret :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT c.id_contact, d.id_demande, d.date_demande, c.nom, c.email1 
    FROM demande d LEFT JOIN contact c ON d.contact_id = c.id_contact
    id_contact….id_demande….date_demande……….nom……...email1
    -----------------------------------------------------------------------------------------------
    1…………….......1…………...….25/01/2006 ……......TOTO..….toto@free.fr
    2…………….......2……………....12/04/2006 . ….......LEON…...leon@wanadoo.fr
    3…………......….3……………....13/05/2006 . …………TOTO .….toto@free.fr
    4…………......….4…………...….13/06/2006 . …………PAUL1 .…paul@neuf.fr
    4…………......….5……………....03/09/2006 . …………PAUL1 .…paul@neuf.fr
    4…………......….6…………...….04/09/2006 …………..PAUL1 .…paul@neuf.fr
    4…………......….7……………....02/01/2007 …………..PAUL2 .…paul@neuf.fr

    Voila ce que je souhaiterais obtenir :

    id_contact….id_demande..date_demande……nom…….email1……………......nb
    ---------------------------------------------------------------------------------------------------
    1………….....….1…………….25/01/2006 ……....…TOTO……toto@free.fr………..2
    2……………......2…………….12/04/2006 . ……....LEON .….leon@wanadoo.fr…..1
    3……………......3…………….13/05/2006 . ……....TOTO ...…toto@free.fr…….….2
    4……………......4…………….13/06/2006 . ……....PAUL1 .…paul@neuf.fr………...4
    4………….....….5…………….03/09/2006 . ……....PAUL1 .…paul@neuf.fr………...4
    4………….….....6…………….04/09/2006 ……....…PAUL1 .…paul@neuf.fr………...4
    4…………….....7…………….02/01/2007 …….....…PAUL2 .…paul@neuf.fr………...4


    Or si nous faisons
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT MIN(d.date_demande), c.nom, c.email1, COUNT(d.id_demande) AS Nb
    FROM contact c
      LEFT JOIN demande d ON d.contact_id = c.id_contact 
    GROUP BY c.nom
    On obtient :

    MIN( d . date_demande )…nom…………email1……………….…Nb
    ---------------------------------------------------------------------------------------------
    12/04/2006………………………LEON………leon@wanadoo.fr………..1
    NULL…………………………….....NOEMIE…...nono@club.fr……………0
    13/06/2006……………………….PAUL1……..paul@neuf.fr………….…3
    02/01/2007……………………….PAUL2……..paul@neuf.fr…………….1
    25/01/2006……………………….TOTO………toto@free.fr……………..2

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
     
    -- 
    -- Structure de la table `contact`
    -- 
     
    CREATE TABLE `contact` (
      `id_contact` int(11) NOT NULL auto_increment,
      `nom` varchar(100) collate latin1_general_ci NOT NULL default '',
      `email1` varchar(250) collate latin1_general_ci NOT NULL default '',
      PRIMARY KEY  (`id_contact`),
      KEY `email1` (`email1`)
    ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=27376 ;
     
    -- 
    -- Contenu de la table `contact`
    -- 
     
    INSERT INTO `contact` (`id_contact`, `nom`, `email1`) VALUES 
    (1, 'TOTO', 'toto@free.fr'),
    (2, 'LEON', 'leon@wanadoo.fr'),
    (3, 'TOTO', 'toto@free.fr'),
    (4, 'PAUL1', 'paul@neuf.fr'),
    (5, 'NOEMIE', 'nono@club.fr'),
    (6, 'PAUL2', 'paul@neuf.fr');
     
    -- --------------------------------------------------------
     
    -- 
    -- Structure de la table `demande`
    -- 
     
    CREATE TABLE `demande` (
      `id_demande` int(11) NOT NULL auto_increment,
      `date_demande` date NOT NULL default '0000-00-00',
      `contact_id` int(11) NOT NULL default '0',
      PRIMARY KEY  (`id_demande`)
    ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci PACK_KEYS=0 AUTO_INCREMENT=23411 ;
     
    -- 
    -- Contenu de la table `demande`
    -- 
     
    INSERT INTO `demande` (`id_demande`, `date_demande`, `contact_id`) VALUES 
    (1, '2006-01-25', 1),
    (2, '2006-04-12', 2),
    (3, '2006-05-13', 3),
    (4, '2006-06-13', 4),
    (5, '2006-09-03', 4),
    (6, '2006-09-04', 4),
    (7, '2007-01-02', 6);

  6. #6
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    Autrement dit, tu veux mettre le nb total de demandes en face de chaque demande individuelle ?

    Par ailleurs, tu veux éliminer les contacts sans demande ? Il faut dans ce cas utiliser un INNER JOIN au lieu du LEFT JOIN.

    Pour éviter la sous-requête, tu peux utiliser une seconde instance de la table demandes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN demande d   ON  d.contact_id = c.id_contact
      INNER JOIN demande d2 ON d2.contact_id = c.id_contact
    GROUP BY c.id_contact, d.id_demande

  7. #7
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    Hélas Antoun ta proposition ne donne pas le résultat escompté.
    Ta proposition donne le résultat suivant :
    id_contact….id_demande..date_demande……nom…….email1……………......nb
    ---------------------------------------------------------------------------------------------------
    1………….....….1…………….25/01/2006 ……....…TOTO……toto@free.fr………....1
    2……………......2…………….12/04/2006 . ……....LEON .….leon@wanadoo.fr…..1
    3……………......3…………….13/05/2006 . ……....TOTO ...toto@free.fr……...….1
    4……………......4…………….13/06/2006 . ……....PAUL1 .…paul@neuf.fr………...3
    4………….....….5…………….03/09/2006 . ……....PAUL1 .…paul@neuf.fr………...3
    4………….….....6…………….04/09/2006 ……....…PAUL1 .…paul@neuf.fr………...3
    4…………….....7………….….02/01/2007 ……....…PAUL2 .…paul@neuf.fr………...1
    Or je souhaite pour chaque demande avoir le total de demandes effectuées par une adresse email donnée (voir msg précedent)
    le code ci-dessous donne le bon résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT c.id_contact, d.id_demande, d.date_demande, c.nom, c.email1,(select count(c2.email1) from contact c2, demande d2 where c2.email1=c.email1 and c2.id_contact=d2.contact_id) as nb FROM demande d LEFT JOIN contact c ON d.contact_id = c.id_contact
    Malheureusement, ce code sur une vrai base plante. Si on prend un base avec quelques enregistrements, ok, mais sur une base avec des milliers d'enregistrements, : pas bon ...

    requête SQL: Modifier
    SHOW TABLE STATUS LIKE 'demande';
    MySQL a réponduocumentation
    #2006 - MySQL server has gone away

    je ne sais pas quoi faire.

  8. #8
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    Donc tu veux considérer que deux personnes qui ont le même e-mail sont la même personne ?

    Bon, c'est le même principe, tu transformes ta sous-requête corrélée (donc mal optimisée par MySQL) en jointure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN demande d   ON  d.contact_id = c.id_contact
      INNER JOIN contact c2 ON c.email1 = c2.email1
      INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande
    ORDER BY d.date_demande
    J'ai cru comprendre que tu voulais éliminer Noémie, pourtant tu utilises un LEFT JOIN ? Si jamais tu veux faire apparaître les zéros, il faut faire le COUNT sur d2.id_demande :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(d2.id_demande) AS nb
    FROM contact c
      LEFT JOIN demande d   ON  d.contact_id = c.id_contact
      LEFT JOIN contact c2 ON c.email1 = c2.email1
      LEFT JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande
    ORDER BY d.date_demande

  9. #9
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    Merci,

    Ta proposition fonctionne bien. Malheureusement, en production, mysql plante.

  10. #10
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    1/ nous sommes bien d'accord que cette requête n'est pas dans une boucle et n'est exécutée qu'une seule fois ?

    2/ y a-t-il un index sur chacune des colonnes suivantes ?
    contact_id
    id_contact
    email1
    date_demande

    3/ as-tu choisi la version INNER JOIN (plus rapide) ou LEFT JOIN ?

    4/ combien de lignes doivent être ramenées en prod ?

  11. #11
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    Bonjour Antoun,

    Tout d'abord, merci de t'intéresser à ma problématique d'optimisation.
    Pour répondre à tes questions de diagnostic :
    1) Nous somme d'accord, on n'utilise pas de boucle
    2)
    contact_id est ma clé primaire de la table contact
    id_contact : rien pas indexé (table demande)
    email1 est indexé (table contact)
    date_demande rien (pas indexé)
    3) J'ai choisi la requête avec INNER dans la mesure où je dois afficher que les demandes par date donc les contacts qui ont effectué une demande.

    4) 21256 lignes à ramener . Mais j'affiche uniquement 20 demandes par page en faite ce qui réduit en réalité la requête

    ci-dessous le résultat de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    EXPLAIN SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN demande d   ON  d.contact_id = c.id_contact
      INNER JOIN contact c2 ON c.email1 = c2.email1
      INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande
    ORDER BY d.date_demande
    id...select_type....table..type.....possible_keys…….key..........key_len....ref……………........rows.....Extra
    1…..SIMPLE………...d……...ALL…....NULL……………......NULL........NULL……...NULL…………........21256….Using temporary; Using filesort
    1…..SIMPLE………...d2…....ALL…....NULL……………......NULL……….NULL……....NULL………….......21256 ..
    1…..SIMPLE………...c……...eq_ref..PRIMARY,email1….PRIMARY…4……….......x.d.contact_id ..1…..
    1…..SIMPLE………...c2…....eq_ref..PRIMARY,email1….PRIMARY…4……….......x.d2.contact_id..1…….....Using where

    Voilà Docteur !


    Tes questions étant pertinentes, je viens de mettre un index sur mon contact_id de la table demande et oh surprise : mysql ne plante plus et affiche le résultat rapidement !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    explain SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN demande d   ON  d.contact_id = c.id_contact
      INNER JOIN contact c2 ON c.email1 = c2.email1
      INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande ORDER BY d.date_demande
    donne maintenant :
    id...select_type....table..type.....possible_keys………….key..............key_len.....ref……………......rows.....Extra
    1…..SIMPLE……...…d……..ALL….....contact_id……………..NULL................NULL…….NULL………....….21256…Using temporary; Using filesort
    1…..SIMPLE………...c….....eq_ref...PRIMARY,email1 …...PRIMARY …........4…....…x.d.contact_id .…1………….
    1…..SIMPLE………...c2…...ref………..PRIMARY,email1…...email1………........252………..x.c.email1……..1…..
    1…..SIMPLE………...d2…...ref………contact_id …...........contact_id….........…4……….x.c2.id_contact....1……Using index

    Peut-on faire mieux ?

    Hum je ne comprends rien …

    Si pour afficher mes résultats j’utilise une requete mysql dans une boucle, les résultats sont affichés beaucoup plus rapidement que si j’utilise une simple requête mysql.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    < ?
    $requete = ‘SELECT d.id_demande, d.date_demande, c.nom, c.email1 FROM demande d LEFT JOIN contact c ON d.contact_id = c.id_contact ORDER BY d.date_demande DESC LIMIT 0,20’;
     
    $resultat = ExecRequete ($requete, $connexion); // avec ExecRequete une fonction qui utilise mysql_query() et les paramètres de connexion à la base Mysql qui vont bien.
     
     
    while($demandeC = mysql_fetch_object ($resultat));
     
    {
    $date_demande_fr = date("d-M-y", strtotime($demandeC->date_demande));
     
    // Pour trouver les emails présents plusieurs dans la base
    $existance = ExecRequete('SELECT count(contact.email1 )  AS nb FROM contact JOIN demande ON demande.contact_id = contact.id_contact WHERE contact.email1 = \''.$demandeC->email1.'\'', $connexion); 
     
    	$nb_email = mysql_result($existance, 0);
    	if ($nb_email > 1 ){
    	$Nb = '('.$nb_email.')';}
    	else {$Nb ='';}
     
    echo '<TR><TD>',$date_demande_fr,
    '<TD>',$demandeC->nom ,
    '<TD>',$demandeC->email1,' ',$Nb,'</TD></TR>';
    }
     
    ?>
    Les pages s’affichent en moyenne en 0,20 secondes

    Alors que si j'utilise une seule requête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    < ?
    $requete = ‘SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN demande d   ON  d.contact_id = c.id_contact
      INNER JOIN contact c2 ON c.email1 = c2.email1
      INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande
    ORDER BY d.date_demande DESC LIMIT 0,20 ’ ;
     
    $resultat = ExecRequete ($requete, $connexion); // avec ExecRequete une fonction qui utilise mysql_query() et les paramètres de connexion à la base Mysql qui vont bien.
     
     
    while($demandeC = mysql_fetch_object ($resultat));
     
    {
    $date_demande_fr = date("d-M-y", strtotime($demandeC->date_demande));
     
    if ($demandeC->nb > 1 ){
    	$Nb = '('.$demandeC->nb.')';}
    	else {$Nb ='';} 
    echo '<TR><TD>',$date_demande_fr,
    '<TD>',$demandeC->nom ,
    '<TD>',$demandeC->email1,' ',$Nb,'</TD></TR>';
    }
     
    ?>
    Les pages s’affichent en moyenne entre 3,8 secondes ….
    ????

  12. #12
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    C'est normal, avec ta limite de 20, il n'y a que 20 itérations de la sous-requête, ce qui n'est pas grand chose. Avec la requête complète, il calcule le regroupement sur toute la table, puis il y garde les 20 premières lignes.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN (SELECT contact_id, date_demande FROM demande d3 ORDER BY d3.date_demande LIMIT 0, 20)  AS d ON  d.contact_id = c.id_contact
      INNER JOIN contact c2 ON c.email1 = c2.email1
      INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande

  13. #13
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    hum, je vais devoir sérieusement revoir mes connaissances en matière de jointure, .... je suis un peu beaucoup dépassée

    J'ai essayé ton dernier code, mais Mysql me dit :
    #1054 - Unknown column 'd.id_demande' in 'field list'

  14. #14
    Rédacteur/Modérateur

    Avatar de Antoun
    Homme Profil pro
    Architecte décisionnel
    Inscrit en
    Octobre 2006
    Messages
    6 284
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte décisionnel
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2006
    Messages : 6 284
    Points : 11 739
    Points
    11 739
    Par défaut
    Citation Envoyé par tavarlindar
    hum, je vais devoir sérieusement revoir mes connaissances en matière de jointure, .... je suis un peu beaucoup dépassée
    Plutôt que de partir de toute la table demande, j'utilise une sous-requête qui ne prend que les 20 demandes les plus récentes. Les jointures entre cette sous-requête et les autres tables sont les mêmes...

    Citation Envoyé par tavarlindar
    J'ai essayé ton dernier code, mais Mysql me dit :
    #1054 - Unknown column 'd.id_demande' in 'field list'
    Désolé, j'ai oublié de faire remonter cette colonne depuis la sous-requête :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT c.id_contact, d.id_demande,
      d.date_demande, c.nom, c.email1, COUNT(*) AS nb
    FROM contact c
      INNER JOIN (
        SELECT contact_id, id_demande, date_demande 
        FROM demande d3 
        ORDER BY d3.date_demande 
        LIMIT 0, 20
        )  AS d ON  d.contact_id = c.id_contact
      INNER JOIN contact c2 ON c.email1 = c2.email1
      INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande

  15. #15
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    Merci beaucoup Antoun pour le temps que tu me consacres.
    J'ai transposé ton code à mon cas (qui en réalité fait intervenir plusieurs tables).

    Malheureusement, j'ai de nouveau un problème.
    En effet, je n'arrive pas à mettre une clause WHERE.

    La requête ci-dessous est une requête générale :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    SELECT c.id_contact, d.id_demande,  d.date_demande, c.nom, c.pays, c.email1, COUNT( * ) AS nb
    FROM contact c
    INNER JOIN (
    SELECT contact_id, id_demande, date_demande
    FROM demande d3
    ORDER BY d3.id_demande DESC
    LIMIT 0 , 20
    ) AS d ON d.contact_id = c.id_contact
    INNER JOIN contact c2 ON c.email1 = c2.email1
    INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    GROUP BY c.email1, d.id_demande  ORDER BY id_demande DESC
    Je souhaite ne selectionner que les demandes effectuées par des belges.
    J'ai donc essayé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT c.id_contact, d.id_demande,  d.date_demande, c.nom, c.pays, c.email1, COUNT( * ) AS nb
    FROM contact c
    INNER JOIN (
    SELECT contact_id, id_demande, date_demande
    FROM demande d3
    ORDER BY d3.id_demande DESC
    LIMIT 0 , 20
    ) AS d ON d.contact_id = c.id_contact
    INNER JOIN contact c2 ON c.email1 = c2.email1
    INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    WHERE c.pays='BE'
    GROUP BY c.email1, d.id_demande  ORDER BY id_demande DESC
    Le code ne plante pas, mais mysql me retourne 0 enregistrement.
    Pays = code iso du pays donc pays=FR pour les contacts qui sont français, BE pour les belges, etc ...

    ????

    Sachant que je souhaite faire des WHERE c.pays='FR' AND c.nom like '%DUPONT%' AND c.email1='toto@tata.fr' AND ....

    Que dois-je faire ?

    J'ai remarqué que ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    SELECT c.id_contact, d.id_demande,  d.date_demande, c.nom, c.pays, c.email1, COUNT( * ) AS nb
    FROM contact c
    INNER JOIN (
    SELECT contact_id, id_demande, date_demande
    FROM demande d3
    ORDER BY d3.id_demande DESC
    LIMIT 0 , 20
    ) AS d ON d.contact_id = c.id_contact
    INNER JOIN contact c2 ON c.email1 = c2.email1
    INNER JOIN demande d2 ON d2.contact_id = c2.id_contact
    WHERE c.email1='nom@provider.fr'
    GROUP BY c.email1, d.id_demande  ORDER BY id_demande DESC
    retourne le résultat escompté.
    Normal sans doute, mais je ne comprends pas pourquoi ...

    J'ai encore besoin de tes lumières s'il te plait.


  16. #16
    Membre régulier Avatar de tavarlindar
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 262
    Points : 97
    Points
    97
    Par défaut
    oups, j'ai posté trop vite ...

    évidemment que l'on peut mettre une clause WHERE ... simplement oublié quand mettant un LIMIT 0 , 20 cela réduissait d'autant les résultats

    Merci encore une fois Antoun pour ton aide précieuse.

    Bien à Toi

    Tavar

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comment optimiser une requete Update SQL ?
    Par Battosaiii dans le forum SQL
    Réponses: 3
    Dernier message: 20/07/2011, 14h51
  2. [AC-2010] comment faire une requete avec la fonction *
    Par Martintin dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 16/01/2011, 20h46
  3. [AC-2007] Comment faire une requete avec 23 critères "ou" sur colonnes differentes
    Par sebing dans le forum Requêtes et SQL.
    Réponses: 10
    Dernier message: 14/09/2010, 20h33
  4. comment executer une requete avec une boucle?
    Par medsine dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 30/05/2008, 15h45
  5. [Access] Comment créer une requete avec la date
    Par Daniela dans le forum Langage SQL
    Réponses: 1
    Dernier message: 27/09/2006, 12h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo