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 :

Optimisation d'une requête


Sujet :

Requêtes MySQL

  1. #1
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut Optimisation d'une requête
    Bonjour à tous,

    J'ai une table en InnoDB qui a 2 millions d'enregistrements. (C'est uniquement parce que j'ai fait tourner un programme de test toute la nuit)

    Etant donné que cette table est énorme, je me suis aperçu qu'une de mes requêtes est lente ( +60s):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    SELECT MAX(IndexedColumn) FROM ( myBigTable) WHERE aColumn=1
    Pour info, cette requête est bien instantanée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    SELECT MAX(IndexedColumn) FROM  ( myBigTable)
    Dans la colonne "aColumn", il n'y a que 2 valeurs différentes (1 et 2)
    Il y a 2 millions d’enregistrements ayant "aColumn" avec la valeur 1 et 4000 ayant la valeur 2.

    Vu le temps, j'ai l'impression que mysql utilise d'abord la colonne where, puis sur tous les résultats prends le max.
    Alors qu'il serait plus performant de retourner le premier max ou la condition est vrai.

    Existe-il une autre façon d'écrire cette requête ?

    Merci.

  2. #2
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Mai 2002
    Messages
    3 173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 173
    Points : 5 345
    Points
    5 345
    Par défaut
    Alors qu'il serait plus performant de retourner le premier max ou la condition est vrai.
    Et comment il devine que la condition est vrai ?

    Donc oui il est forcé de faire un scannage de table puis de trouver le max de ceci.

    Et je pense qu'un index du type : (aColumn, IndexedColumn) ne changera pas grand chose à cause de la diversité des données.

  3. #3
    Membre expert
    Avatar de Maljuna Kris
    Homme Profil pro
    Retraité
    Inscrit en
    Novembre 2005
    Messages
    2 613
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 72
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Retraité
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 613
    Points : 3 950
    Points
    3 950
    Par défaut
    Saluton,
    Vu le contexte, essaye comme cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT MAX(IndexedColumn) FROM ( myBigTable) GROUP BY aColumn
    Cela retournera certes deux lignes, mais peut-être plus vite.
    Kie lumo eksistas ankaŭ ombro troviĝas. L.L. Zamenhof
    articles : Comment émuler un tableau croisé [quasi] dynamique
    et : Une énigme mathématique résolue avec MySQL
    recommande l'utilisation de PDO (PHP5 Data Objects)

  4. #4
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut
    Citation Envoyé par Maljuna Kris Voir le message
    Saluton,
    Vu le contexte, essaye comme cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT MAX(IndexedColumn) FROM ( myBigTable) GROUP BY aColumn
    Cela retournera certes deux lignes, mais peut-être plus vite.
    Je viens de tester avec le "GROUP BY" et c'est le même temps.

  5. #5
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut
    Citation Envoyé par punkoff Voir le message
    Et comment il devine que la condition est vrai ?

    Donc oui il est forcé de faire un scannage de table puis de trouver le max de ceci.

    Et je pense qu'un index du type : (aColumn, IndexedColumn) ne changera pas grand chose à cause de la diversité des données.
    Je me suis peut être mal exprimé.

    Je pense que si il existe une requête qui permet de faire faire à Mysql quelque chose comme ça ce serait plus rapide (La requête n'est pas bonne, c'est juste pour donner une idée):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    lastmax=MAX;
    while(1) {
     
      SELECT aColumn, MAX(IndexedColumn) AS lastMax FROM ( myBigTable) WHERE IndexedColumn<lastMax;
     
      if(aColumn==1) {
        return lastmax;
      }
    }
    Dans mon cas, il aurait au pire 4000 MAX à chercher et 4000 conditions à tester. (Ce qui doit probablement prendre moins de 60s)

  6. #6
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Mai 2002
    Messages
    3 173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 173
    Points : 5 345
    Points
    5 345
    Par défaut
    Citation Envoyé par Fred_34 Voir le message
    Je me suis peut être mal exprimé.

    Je pense que si il existe une requête qui permet de faire faire à Mysql quelque chose comme ça ce serait plus rapide (La requête n'est pas bonne, c'est juste pour donner une idée):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    lastmax=MAX;
    while(1) {
     
      SELECT aColumn, MAX(IndexedColumn) AS lastMax FROM ( myBigTable) WHERE IndexedColumn<lastMax;
     
      if(aColumn==1) {
        return lastmax;
      }
    }
    Dans mon cas, il aurait au pire 4000 MAX à chercher et 4000 conditions à tester. (Ce qui doit probablement prendre moins de 60s)

    Dans votre 1er poste vous dites que 4000 lignes ont la valeur 2 et .....1996000 ont la valeur 1.
    Ici vous cherchez le max de la valeur qui a 1. Donc non votre boucle while est encore plus archaïque que ce qu le sgbd fait et il n'y a pas moyen de l'optimiser. testez avec un index comme je vous l'ai indiqué mais il y a de grande chance qu'il ne l'utilise pas.



    En fait vous cherchez la 1ere ligne qui dispose de la condition aColumn = 1 ?

    A quoi sert le MAX dans votre 2eme exemple ?

  7. #7
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Teste si cette requête est plus rapide :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT IndexedColumn 
    FROM myBigTable 
    WHERE aColumn = 1
    Si c'est le cas, essaie ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    CREATE TEMPORARY TABLE temp
    SELECT IndexedColumn 
    FROM myBigTable 
    WHERE aColumn = 1;
     
    SELECT MAX(IndexedColumn)
    FROM temp;
    Sinon, vu ce que tu dis ici :
    C'est uniquement parce que j'ai fait tourner un programme de test toute la nuit
    Est-ce que par hasard tes données sont ordonnées avec d'abord les valeurs 1 puis les valeurs 2 et que tu cherches le dernier id auto-incrémenté correspondant à la valeur 1 ?

    Si cette supposition est juste et si tu n'as pas supprimé des données, ceci devrait être plus rapide :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    CREATE TEMPORARY TABLE temp
    SELECT IndexedColumn
    FROM myBigTable
    WHERE aColumn = 2;
     
    SELECT MIN(IndexedColumn) - 1 AS last_id_with_value_1
    FROM temp
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  8. #8
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Mai 2002
    Messages
    3 173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 173
    Points : 5 345
    Points
    5 345
    Par défaut
    Et ceci ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    select IndexedColumn 
    from bigtable
    where aColumn = 1
    order by IndexedColumn  desc
    limit 1
    Si IndexedColumn est vraiment indexé, logiquement il devrait faire un scan d'index en partant du max et checker la condition après.

    En viendra le limit.

  9. #9
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    Teste si cette requête est plus rapide :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT IndexedColumn 
    FROM myBigTable 
    WHERE aColumn = 1
    Cette requête prends 1,7s


    Citation Envoyé par CinePhil Voir le message
    Si c'est le cas, essaie ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    CREATE TEMPORARY TABLE temp
    SELECT IndexedColumn 
    FROM myBigTable 
    WHERE aColumn = 1;
     
    SELECT MAX(IndexedColumn)
    FROM temp;
    Aye : Le CREATE à pris 75 secondes et le SELECT 2,5s

    J'ai compris pourquoi la première requete avait été rapide :
    - Je fais les tests des différentes requêtes avec workbench. Il avait ajouté limit 0,1000 au select, d'ou les 1.7s. Avec un limit 0,3000000 on retombe dans les +60s


    Citation Envoyé par CinePhil Voir le message
    Est-ce que par hasard tes données sont ordonnées avec d'abord les valeurs 1 puis les valeurs 2 et que tu cherches le dernier id auto-incrémenté correspondant à la valeur 1 ?
    Non, ce n'est pas le cas.

  10. #10
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut
    Citation Envoyé par punkoff Voir le message
    Et ceci ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    select IndexedColumn 
    from bigtable
    where aColumn = 1
    order by IndexedColumn  desc
    limit 1
    Si IndexedColumn est vraiment indexé, logiquement il devrait faire un scan d'index en partant du max et checker la condition après.

    En viendra le limit.
    Cette requête donne le bon résultat et en 2s !

    Maintenant, je fais face à un dilemme. Dois-je généraliser cette requête partout ou je fais des SELECT MAX() avec une condition WHERE qui ne porte pas sur l'index recherché...

    En tout cas, merci à tous les deux pour votre aide.

  11. #11
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Nous ne t'avons pas conseillé de mettre un index sur aColumn car elle ne peut prendre que deux valeurs et l'index ne serait probablement pas utilisé. Ceci dit, tu peux essayer.

    Si par contre tu as une recherche sur une autre colonne comptant un grand nombre de valeurs, l'index sera utilisé et ce genre de requête devrait être beaucoup plus rapide.

    À tester au cas par cas.

    Il me semble quand même un peu long qu'une recherche d'un MAX sur 2 millions de lignes mette autant de temps, même sans index sur la colonne cherchée !
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  12. #12
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    Nous ne t'avons pas conseillé de mettre un index sur aColumn car elle ne peut prendre que deux valeurs et l'index ne serait probablement pas utilisé. Ceci dit, tu peux essayer.

    Si par contre tu as une recherche sur une autre colonne comptant un grand nombre de valeurs, l'index sera utilisé et ce genre de requête devrait être beaucoup plus rapide.

    À tester au cas par cas.

    Il me semble quand même un peu long qu'une recherche d'un MAX sur 2 millions de lignes mette autant de temps, même sans index sur la colonne cherchée !
    J'avais commencé par mettre un index sur aColumn, mais ça n'avait pas changé grand chose. Ce qui a priori est normal vu qu'un index sur une colonne qui a très peu de valeurs possibles ne sert à rien.

    Pour les performances : Je fais ces tests sur un PC portable et je prends bien soin d’arrêter mysql entre chaque tests afin qu'il n'y ai rien en cache.

    En fait, au lieu de "généraliser cette requête a tous les SELECT MAX() avec une condition WHERE qui ne porte pas sur l'index recherché"
    Ce serait plutôt "généraliser cette requête partout ou je fais des SELECT MAX(anIndexedColumn) avec une condition WHERE qui ne porte pas sur un index"

  13. #13
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par Fred_34 Voir le message
    avec une condition WHERE qui ne porte pas sur un index"
    Si cette colonne non indexée a un grand nombre de valeurs différentes, il faut envisager de l'indexer.
    Attention toutefois à ce que la taille des index ne dépasse pas la taille de la table ; ça deviendrait contre-productif.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  14. #14
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 445
    Points : 622
    Points
    622
    Par défaut
    Voici les explains des différentes requêtes que j'ai testé. (Désolé pour le formatage)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    explain SELECT max(indexedColumn)
    FROM myBigTable
    id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
    1, SIMPLE, NULL, NULL, NULL, NULL, NULL, NULL,NULL,"Select tables optimized away"


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    explain SELECT indexedColumn
    FROM myBigTable
    WHERE nonIndexedColumn = 1
    ORDER BY indexedColumn DESC
    LIMIT 1
    id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
    1, SIMPLE, myBigTable,index,NULL,indexedColumn_IDX,4,NULL,1,"Using where"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    explain SELECT max(indexedColumn)
    FROM myBigTable
    WHERE nonIndexedColumn = 1
    id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
    1,SIMPLE,myBigTable,ALL,NULL,NULL,NULL,NULL,212130,"Using where"

    J'ai l'impression que Mysql n'utilise pas le fait que indexedColumn soit un index.

    D'ailleurs si on fait un explain sur cette requête :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    explain SELECT max(anotherNonIndexedColumn)
    FROM myBigTable
    WHERE nonIndexedColumn = 1
    On obtient le même résultat :
    id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
    1,SIMPLE,myBigTable,ALL,NULL,NULL,NULL,NULL,212130,"Using where"

    Entre temps, j'ai supprimé cette grosse table. Je fais tourner mon programme de test pour récréer une table de plusieurs millions d'enregistrements et pouvoir vérifier tout ça.

    Edit :
    J'obtiens bien les mêmes temps de réponses pour SELECT MAX(indexedColumn) WHERE... et SELECT MAX(nonIndexedColumn) WHERE...

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

Discussions similaires

  1. Optimisation d'une requête
    Par Louis-Guillaume Morand dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 20/12/2005, 18h21
  2. Optimisation d'une requête d'insertion
    Par fdraven dans le forum Oracle
    Réponses: 15
    Dernier message: 01/12/2005, 14h00
  3. Optimisation d'une requête patchwork
    Par ARRG dans le forum Langage SQL
    Réponses: 1
    Dernier message: 11/09/2005, 15h23
  4. optimisation d'une requête avec jointure
    Par champijulie dans le forum PostgreSQL
    Réponses: 8
    Dernier message: 07/07/2005, 09h45
  5. [DB2] Optimisation d'une requête
    Par ahoyeau dans le forum DB2
    Réponses: 7
    Dernier message: 11/03/2005, 17h54

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