Aidez moi à optimiser cette requete plize!

Aidez moi à optimiser cette requete plize! - SQL/NoSQL - Programmation

Marsh Posté le 09-09-2005 à 15:31:03    

Bonjour à tous,
Je suis en train de réaliser une requete assez conséquente et je suis arrivé au bout de mes compétences en terme d'optimisation, j'ai tout essayé : WHERE, HAVING , sous requetes imbriquées...:
Je suis comme meme passer de 5 minutes à 40 secondes puis à 15 secondes puis à 7 et ce sur un Bi Proc XEON...
Sachant que le client possede un pauvre athlon 1.3...je crains le pire...
Voila ma requete aidez moi :) (le MCD ne sera d'aucune utilité, c'est la syntaxe qui n'est pas optimale je pense...)
 

Code :
  1. SELECT SQL_CALC_FOUND_ROWS DATE_FORMAT( actions.date_recontact, '%d/%m/%Y' ) AS date_recontact, lignes_etape.resultat, contacts.id_contact, contacts.nom, societes_specifiques.topage, societes_specifiques.score_societe, societes.id_societe, script_etapes.groupe_etape, s_est_deplace, operation_a_effectuer, SUBSTRING( actions.heure_recontact, 1, 5 ) AS heure_recontact, societes.nom, actions.id_action, actions.id_contact, contacts.fonction_autre
  2. FROM actions
  3. LEFT JOIN lignes_etape ON actions.resultat = lignes_etape.id_ligne_etape
  4. INNER JOIN contacts ON actions.id_contact = contacts.id_contact
  5. AND contacts.plus_dans_la_soc =0
  6. INNER JOIN contacts_specifiques ON contacts_specifiques.id_contact = contacts.id_contact
  7. INNER JOIN societes ON contacts.id_societe = societes.id_societe
  8. INNER JOIN societes_specifiques ON societes_specifiques.id_societe = societes.id_societe
  9. AND societes_specifiques.id_entite =2
  10. INNER JOIN script_etapes ON actions.etape = script_etapes.id_script_etape
  11. INNER JOIN societes_users ON societes_users.id_societe = societes.id_societe
  12. AND societes_users.id_campagne =1
  13. WHERE actions.enregistrement_ok =1
  14. AND actions.sommeil =0###############
  15. AND actions.id_contact
  16. IN (
  17. SELECT A3.id_contact
  18. FROM actions AS A3
  19. INNER JOIN script_etapes S1 ON A3.etape = S1.id_script_etape
  20. WHERE S1.groupe_etape =3
  21. AND A3.resultat =0
  22. HAVING 0 = (
  23. SELECT COUNT( * )
  24. FROM actions A4
  25. INNER JOIN script_etapes ON A4.etape = script_etapes.id_script_etape
  26. WHERE script_etapes.groupe_etape =3
  27. AND A4.id_contact = A3.id_contact
  28. AND A3.id_action <> A3.id_action )
  29. )
  30. AND actions.id_action = (
  31. SELECT MAX( id_action )
  32. FROM actions AS A2
  33. INNER JOIN script_etapes AS S3 ON A2.etape = S3.id_script_etape
  34. WHERE A2.id_contact = actions.id_contact
  35. AND S3.groupe_etape =1
  36. )


Message édité par PETOZAK le 09-09-2005 à 16:50:53
Reply

Marsh Posté le 09-09-2005 à 15:31:03   

Reply

Marsh Posté le 11-09-2005 à 02:04:38    

Comme je dis toujours :
"Remplace ton IN par un EXISTS".
 
Le coup du "HAVING 0 = count (*) ..." je ne suis pas convaincu : le HAVING s'effectue lors du postprocessing, c'est à dire que c'est un filtre appliqué une fois la requête terminée, donc tu vas lire inutilement les lignes qui ne seront pas rammenée.
 
Sinon, là où tu pourras optimiser un maximum, c'est certainement sur les index : vérifie qu'ils sont "parfaits" pour ta requête (l'ensemble des champs des critères de filtre doivent participer à un index, pas un champ de moins ni de plus).
Et enfin, regarde si ton SGBD connait ce qu'on appelle les "query hints", qui te permettront de lui indiquer les meilleurs index en outrepassant les décisions de l'optimiseur.
 
Dans tous les cas, vérifie que tu n'as pas dans ton plan d'éxécution :
- RANGE SCAN
- TEMPORARY TABLE
- FULL SCAN (sauf si t'as un endroit où tu ne fais volontairement aucun filtre)

Reply

Marsh Posté le 11-09-2005 à 10:43:08    

Je ne serai pas aussi catégorique, un FULL SCAN peut souvent être plus rapide qu'un passage par un index (exemple : quand on récupère une grosse partie d'une table).
 
Maintenant, c'est clairement la partie dans ton IN qu'il faudrait réécrire / simplifier.
Suivant ton SGBD, ça peut valoir le coup de mettre des sous-requêtes dans le FROM directement et de faire des jointures dessus, plutôt que des IN / EXISTS.

Reply

Marsh Posté le 13-09-2005 à 09:41:25    

Salut,
Je voulais savoir si la requete à l'interieur de mon IN est executée àchaque fois ou bien une seule fois?
L'ensemble renvoyé étant statique et ne dépendant pas de la requete mère, ce serait trés dommage qu'elle soit executée à chaque fois , non?

Reply

Marsh Posté le 13-09-2005 à 09:42:20    

pourquoi alors ne pas les mettre dans le from?

Reply

Marsh Posté le 13-09-2005 à 09:47:32    

Arjuna a écrit :

Comme je dis toujours :
"Remplace ton IN par un EXISTS".)


 
----> Ce IN est il exexuter une fois ou plusieurs?
 

Arjuna a écrit :


Le coup du "HAVING 0 = count (*) ..." je ne suis pas convaincu : le HAVING s'effectue lors du postprocessing, c'est à dire que c'est un filtre appliqué une fois la requête terminée, donc tu vas lire inutilement les lignes qui ne seront pas rammenée.


---->,Postprocessing,effectivement, c'est d'ailleurs pour ca que je l'utilise afin d'eviter de le relancer à chaque fois, sion tu propposes quoi? une sous requete?
 

Arjuna a écrit :


Sinon, là où tu pourras optimiser un maximum, c'est certainement sur les index : vérifie qu'ils sont "parfaits" pour ta requête (l'ensemble des champs des critères de filtre doivent participer à un index, pas un champ de moins ni de plus).


---> Les Index tu les assigne pas uniquement pour les clés et les les jointures? :??:  
 

Arjuna a écrit :


Et enfin, regarde si ton SGBD connait ce qu'on appelle les "query hints", qui te permettront de lui indiquer les meilleurs index en outrepassant les décisions de l'optimiseur.


---->Pas sur que mysql le gere?
 

Arjuna a écrit :


Dans tous les cas, vérifie que tu n'as pas dans ton plan d'éxécution :
- RANGE SCAN
- TEMPORARY TABLE
- FULL SCAN (sauf si t'as un endroit où tu ne fais volontairement aucun filtre


---->je connais la commande sous oracle et SQLServer mais pas Mysql, c'est quoi?


Message édité par PETOZAK le 13-09-2005 à 09:48:25
Reply

Marsh Posté le 13-09-2005 à 09:56:51    

betsamee a écrit :

pourquoi alors ne pas les mettre dans le from?


C'est une excellent idée :)
J'y ai pensé au meme moment que toi :)...
J'espere que CA LE FERA

Reply

Marsh Posté le 13-09-2005 à 11:25:46    

perso pour un probleme quasi similaire ca l'a fait gravement pour moi

Reply

Marsh Posté le 13-09-2005 à 15:17:07    

Code :
  1. SELECT SQL_CALC_FOUND_ROWS
  2.        DATE_FORMAT(A.date_recontact, '%d/%m/%Y' ) AS date_recontact,
  3.        LE.resultat, C.id_contact, C.nom,
  4.        SS.topage, SS.score_societe,
  5.        S.id_societe,
  6.        SE.groupe_etape,
  7.        s_est_deplace, operation_a_effectuer,
  8.        SUBSTRING(A.heure_recontact, 1, 5 ) AS heure_recontact,
  9.        S.nom, A.id_action, A.id_contact, C.fonction_autre
  10. FROM   actions A
  11.        LEFT OUTER JOIN lignes_etape LE                  ON A.resultat = LE.id_ligne_etape
  12.        INNER JOIN script_etapes SE                      ON A.etape = SE.id_script_etape
  13.        INNER JOIN contacts C                            ON A.id_contact = C.id_contact
  14.              INNER JOIN contacts_specifiques CS         ON C.id_contact = CS.id_contact
  15.              INNER JOIN societes S                      ON C.id_societe = S.id_societe
  16.                    INNER JOIN societes_specifiques SS   ON S.id_societe = SS.id_societe
  17.                    INNER JOIN societes_users SU         ON S.id_societe = SU.id_societe
  18. WHERE  SU.id_campagne = 1
  19.   AND  SS.id_entite = 2
  20.   AND  A.enregistrement_ok = 1
  21.   AND  C.plus_dans_la_soc = 0
  22.   AND  A.sommeil = 0###############
  23.   AND A.id_contact IN (SELECT A3.id_contact
  24.                        FROM   actions AS A3
  25.                               INNER JOIN script_etapes S1 ON A3.etape = S1.id_script_etape
  26.                        WHERE  S1.groupe_etape = 3
  27.                          AND  A3.resultat = 0
  28.                          AND NOT EXISTS (SELECT 0
  29.                                          FROM   actions A4
  30.                                                 INNER JOIN script_etapes ON A4.etape = script_etapes.id_script_etape
  31.                                          WHERE  script_etapes.groupe_etape =3
  32.                                            AND  A4.id_contact = A3.id_contact
  33.                                            AND  A3.id_action <> A3.id_action)
  34.   AND actions.id_action = (SELECT MAX( id_action )
  35.                            FROM   actions AS A2
  36.                                   INNER JOIN script_etapes AS S3 ON A2.etape = S3.id_script_etape
  37.                            WHERE  A2.id_contact = actions.id_contact
  38.                              AND S3.groupe_etape =1)


 
 
Salut voila le resultat, j'ai divisé par 10 le temps d'execution  :love:  
merci à tous  :hello:

Reply

Marsh Posté le 13-09-2005 à 15:19:13    

pas la force d'analyser ton code , peux tu dire en gros comment tu t'y es pris , ca pourra toujours aider a l'occase
merci

Reply

Sujets relatifs:

Leave a Replay

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