[MySQL] Besoin d'aide pour optimiser une requête

Besoin d'aide pour optimiser une requête [MySQL] - SQL/NoSQL - Programmation

Marsh Posté le 21-09-2010 à 10:01:34    

Bonjour,
 
Je gère un agenda avec des fiches contacts. Je souhaite récupérer le nombre de contacts ayant un rendez-vous entre le 01/08 et le 31/08 pour la première fois depuis 3 ans.
J'ai fait cette requête :

SELECT COUNT(DISTINCT(personne_id)) FROM wcal_events w
WHERE     id_user=55
      AND eventdate BETWEEN '2010-08-01' AND '2010-08-31'
      AND personne_id != 0
      AND event_etat = 5
      AND faut_id = 58
      AND personne_id NOT IN (SELECT personne_id
                              FROM wcal_events
                              WHERE     id_user=55
                                    AND eventdate BETWEEN SUBDATE(w.eventdate,INTERVAL 3 YEAR) AND SUBDATE(w.eventdate,INTERVAL 1 DAY)
                                    AND personne_id != 0
                                    AND event_etat IN (4,5)
                                    AND faut_id=58
                              GROUP BY personne_id);


Mais elle est longue à s'exécuter, et j'ai cherché d'autres solutions sans résultat efficace ... Quelqu'un pourrait-il m'aider ou me conseiller quelque-chose pour optimiser cela ? je pense que le problème vient de la sous-sélection qui est recalculée à chaque fois ... mais comment faire autrement ?
 
MERCI !!!!

Reply

Marsh Posté le 21-09-2010 à 10:01:34   

Reply

Marsh Posté le 22-09-2010 à 16:31:44    

déjà, as tu des indexes sur tous les champs présents dans les clauses WHERE ?

Reply

Marsh Posté le 22-09-2010 à 16:35:48    

Surtout sur eventdate :bounce:


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 23-09-2010 à 13:50:22    

Perso, je mettrais la sous-requête dans le FROM pour en faire une table temporaire...


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 23-09-2010 à 13:57:22    

rufo a écrit :

Perso, je mettrais la sous-requête dans le FROM pour en faire une table temporaire...


 
A tiens, on y gagne en perf à faire ça? J'ai toujours cru que ça devenait intéressant si on lançait souvent la même requête, mais pas si on lançais des requêtes différentes (par exemple s'il change régulièrement le id_user dans son cas). Que ça va d'une certaine manière dans le même sens que les requêtes préparées.  :bounce:


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 23-09-2010 à 14:07:51    

Ca dépend de l'utilisation. Là, sa sous-requête est exécutée pour chaque ID de personne. En la mettant dans le FROM et en lui donnant un nom (temporaire), elle ne sera exécutée qu'une fois... Ca devrait donc faire gagner du temps, non?
 
Après, voir avec NOT EXISTS si ça améliore les perfs : http://dev.mysql.com/doc/refman/5. [...] eries.html


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 23-09-2010 à 14:12:26    

Oui mais il me semble que justement il me semble que la 1ère fois qu'elle sera exécutée, elle sera bien plus lente car demandera la création de la table. Donc on y gagnerait seulement si exactement la même requête était exécutée souvent. Là encore, je suis loin d'être un DBA expert.
Concernant l'histoire de NOT EXISTS, je me rappelle avoir vu qqn qui à benché ça sur ce forum récemment et que ça dépendait pas mal de SGDB et de plusieurs autres params. La encore, le mieux est de voir si un DBA passe par là :D


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 23-09-2010 à 14:32:04    

Ca doit pouvoir se faire assez faciment en remplacant le NOT EXIST avec un LEFT JOIN.
Il faut réecrire la query mais pour ca il me faudrai des donnée en exemple et la structure de la table ... et un truc a bouffer :)
 
A vue de nez ca devrai donner un truc comme ca:

Code :
  1. SELECT COUNT(DISTINCT(w.personne_id))
  2. FROM wcal_events w
  3.     LEFT JOIN wcal_events w2 ON w.personne_id = w2.personne_id
  4.                                 AND w2.id_user=55
  5.                                 AND w2.eventdate BETWEEN SUBDATE(w.eventdate,INTERVAL 3 YEAR) AND SUBDATE(w.eventdate,INTERVAL 1 DAY)
  6.                                 AND w2.personne_id != 0
  7.                                 AND w2.event_etat IN (4,5)
  8.                                 AND w2.faut_id=58
  9. WHERE w.id_user=55
  10.     AND w.eventdate BETWEEN '2010-08-01' AND '2010-08-31'
  11.     AND w.personne_id != 0
  12.     AND w.event_etat = 5
  13.     AND w.faut_id = 58
  14.     AND w2.personne_id IS NULL


 
C'est pas testé vu que j'ai que sql server ici (et que c'est pas du t-sql) et j'ai pas de données de tests.
C'est probablement optimisable aussi, c'est juste pour essayer de mettre l'op sur la voie...

Reply

Marsh Posté le 23-09-2010 à 14:46:19    

C'est pour ça qu'il faut faire des tests. mv1975, tu peux aussi utiliser la commande EXPLAIN histoire de voir déjà quels index sont utilisés et si y'a des tables temporaires qui sont créées.  
Tu peux gagner aussi en perfs en tunant Mysql. Ce script peut aider à faire un diagnostic :http://mysqltuner.pl/mysqltuner.pl


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 23-09-2010 à 14:47:41    

Oliiii a écrit :

Ca doit pouvoir se faire assez faciment en remplacant le NOT EXIST avec un LEFT JOIN.
Il faut réecrire la query mais pour ca il me faudrai des donnée en exemple et la structure de la table ... et un truc a bouffer :)
 
A vue de nez ca devrai donner un truc comme ca:

Code :
  1. SELECT COUNT(DISTINCT(w.personne_id))
  2. FROM wcal_events w
  3.     LEFT JOIN wcal_events w2 ON w.personne_id = w2.personne_id
  4.                                 AND w2.id_user=55
  5.                                 AND w2.eventdate BETWEEN SUBDATE(w.eventdate,INTERVAL 3 YEAR) AND SUBDATE(w.eventdate,INTERVAL 1 DAY)
  6.                                 AND w2.personne_id != 0
  7.                                 AND w2.event_etat IN (4,5)
  8.                                 AND w2.faut_id=58
  9. WHERE w.id_user=55
  10.     AND w.eventdate BETWEEN '2010-08-01' AND '2010-08-31'
  11.     AND w.personne_id != 0
  12.     AND w.event_etat = 5
  13.     AND w.faut_id = 58
  14.     AND w2.personne_id IS NULL


 
C'est pas testé vu que j'ai que sql server ici (et que c'est pas du t-sql) et j'ai pas de données de tests.
C'est probablement optimisable aussi, c'est juste pour essayer de mettre l'op sur la voie...


 
Bien vu le coup du left join à la place du not exits ;)


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 23-09-2010 à 14:47:41   

Reply

Marsh Posté le 28-09-2010 à 10:01:10    

Je reviens tout juste après un arrêt forcé ... Et je découvre avec grand plaisir toutes ces réponses !! Un grand merci à vous tous !!
 
Je peux difficilement vous sortir des données, je travaille sur une copie de données internes à l'entreprise où je travaille, et je ne peux donc pas me permettre de faire sortir ces infos. :/
 
En tout cas, je vais digérer tous vos posts afin d'en sortir quelque chose de super (j'en suis sur).
 
Encore un grand merci !! :)

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed