Optimisation requête (Mysql)

Optimisation requête (Mysql) - SQL/NoSQL - Programmation

Marsh Posté le 27-09-2007 à 15:10:42    

Hello,
 
J'ai la requête suivante qui mets un temps qui me parait invraisemblable (une vingtaine de secondes à être traitée) pour quelques milliers d'enregistrements à parcourir.
Je travaille sur un serveur Mysql 5.
 
Le but de la requête est d'extraire des documents dans un état particulier, et qui ne doivent pas voir de documents rattachés
(concrètement des factures sans avoirs)
 

SELECT `id_doc`  
    FROM `docs`  
    WHERE `id_type` = '4'  
    AND `etat` != '7'
    AND `id_doc` NOT IN ( SELECT `id_doc_pere`
                               FROM `docs`
                               WHERE `id_type` = '5')
    ORDER BY `date_echeance` DESC;


Je n'utilise pas régulièrement les requêtes imbriquées, et vu le temps de traitement de la requête, je me demande si je ne devrais pas faire 2 requetes et effectuer mon filtrage via mon langage de programmation.
 
Est-ce que je m'y prends comme un manche ? ou suis-je limité par autre chose ?


Message édité par PunkRod le 27-09-2007 à 15:10:58
Reply

Marsh Posté le 27-09-2007 à 15:10:42   

Reply

Marsh Posté le 27-09-2007 à 16:20:08    

Le plus "simple" avec un not exits

Code :
  1. SELECT d1.`id_doc`  
  2.    FROM `docs` d1
  3.    WHERE d1.`id_type` = '4'  
  4.    AND d1.`etat` != '7'
  5.    AND NOT exits ( SELECT d2.`id_doc_pere`
  6.                               FROM `docs`d2
  7.                               WHERE d2.`id_type` = '5' AND d1.`id_doc` =d2.`id_doc_pere`)
  8.    ORDER BY d1.`date_echeance` DESC;


La même avec un left outer join + condition à null

Code :
  1. SELECT d1.`id_doc` 
  2.     FROM `docs` d1
  3.     LEFT OUTER JOIN `docs` d2 on d2.id_doc_pere = d1.id_doc and d2.id_type='5'
  4.     WHERE d1.`id_type` = '4' 
  5.     AND d1.`etat` != '7'
  6.     AND d2.id_doc_pere is NULL
  7.     ORDER BY d1.`date_echeance` DESC;


Message édité par anapajari le 27-09-2007 à 16:20:42
Reply

Marsh Posté le 27-09-2007 à 16:27:01    

Merci pour l'astuce, je teste ça dans un instant !!! :jap:
 
edit : ça marche, mais le traitement reste un peu long :/


Message édité par PunkRod le 27-09-2007 à 18:30:08
Reply

Marsh Posté le 28-09-2007 à 08:17:50    

crée un ou pluisuers index

Reply

Marsh Posté le 28-09-2007 à 09:50:04    

J'ai créé l'index sur le type de document (id_type) et là les effets sont instantanés, je suis tombé à 3 secondes pour la requête, ce qui est largement plus acceptable !  
Je repenserais à indexer mes valeurs, merci !  :jap:

Reply

Marsh Posté le 28-09-2007 à 09:51:34    

pour voir ce que fais ta requete, tu peux aussi utiliser EXPLAIN
 

Code :
  1. EXPLAIN SELECT * FROM bidule;


---------------
my flick r - Just Tab it !
Reply

Marsh Posté le 28-09-2007 à 09:51:35    

+1 pour les index, parceque là, tu pourras pas faire mieux en terme de requête (la seconde proposé par anajapari étant la meilleure)

Reply

Marsh Posté le 28-09-2007 à 09:52:22    

moidifie ton index que t'as créé sur "id_type" et rajoute "etat" dans l'index.

Reply

Marsh Posté le 28-09-2007 à 10:13:46    

MagicBuzz a écrit :

+1 pour les index, parceque là, tu pourras pas faire mieux en terme de requête (la seconde proposé par anajapari étant la meilleure)


en toute théorie elles sont equivalentes puisque l'optimizer est censé convertir la 1ere dans quelque chose de très proche de la deuxième.
Maintenant ça reste mysql ( qui reserve ces surprises) donc je garantis pas que ça soit effectivement le cas :)

Reply

Marsh Posté le 28-09-2007 à 10:17:01    

J'aii testé avec Explain justement.
La requête avec le LEFT OUTER JOIN fait au final 2 query de type 'SIMPLE' (c'est l'intitulé de la colonne qui m'est renvoyé :o), là où la requête imbriquée fait une requête 'PRIMARY' et une 'DEPENDENT SUBQUERY'. le left outer join fait gagner quelques ms (bon forcément maintenant que c'est indexé j'ai du mal à bien évaluer les écarts de performances).
 
J'essaie de trouver quelques infos sur ces types de requêtes, pour voir les enjeux en la matière. Préférer des requêtes 'simple' ou pas...

Reply

Marsh Posté le 28-09-2007 à 10:17:01   

Reply

Marsh Posté le 16-11-2007 à 11:02:19    

Hello,

 

Je me permets un petit up pour rester dans le sujet de l'optimisation.
J'ai cette requête qui me pose des soucis :

 
Code :
  1. select a.article, a.titre, a.descri, b.sortie, b.prix, b.flag from articles a, groupes b
  2. where a.article = b.article
  3. and b.pays = '01'
  4. and a.article like '001%' or a.article like 'Z%'
 

Explain :

 
Code :
  1. +----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
  2. | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
  3. +----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
  4. |  1 | SIMPLE      | a     | range | PRIMARY       | PRIMARY | 15      | NULL |   2093 | Using where |
  5. |  1 | SIMPLE      | b     | ALL   | PRIMARY       | NULL    | NULL    | NULL | 138377 | Using where |
  6. +----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
 

Clé primaire en table a sur article et en table b sur article + pays

 

Le select n'en finit pas de tourner et se termine par un "killed" :/

 

Une idée ?


Message édité par Mosca le 16-11-2007 à 11:02:58
Reply

Marsh Posté le 16-11-2007 à 11:10:17    

j'suis jamais certain pour tout ce qui est booléen, donc je mets des parenthèses moi même quand je mixe les AND et OR :
qu'est ce que ça donne si tu modifies ta requête comme ça :
 

Code :
  1. select a.article, a.titre, a.descri, b.sortie, b.prix, b.flag from articles a, groupes b
  2. where a.article = b.article
  3. and b.pays = '01'
  4. and (a.article like '001%' or a.article like 'Z%')

Reply

Marsh Posté le 16-11-2007 à 11:38:34    

profites-en pour ecrire ta jointure explicitement ( et au passage je l'inverserais):

Code :
  1. SELECT
  2. a.article, a.titre, a.descri, b.sortie, b.prix, b.flag
  3. FROM
  4. groupes b
  5. INNER JOIN articles a ON a.article = b.article
  6. WHERE
  7. b.payes = '01'
  8. AND AND (b.article LIKE '001%' OR b.article LIKE 'Z%')


comme ça toutes tes conditions sont sur la même table.

 

Par ailleurs si ton champs article est un très grand varchar, tu gagnerais surement s'pas terrible d'utiliser du like %


Message édité par anapajari le 16-11-2007 à 11:38:42
Reply

Marsh Posté le 16-11-2007 à 11:41:37    

PunkRod a écrit :

j'suis jamais certain pour tout ce qui est booléen, donc je mets des parenthèses moi même quand je mixe les AND et OR :
qu'est ce que ça donne si tu modifies ta requête comme ça :
 

Code :
  1. select a.article, a.titre, a.descri, b.sortie, b.prix, b.flag from articles a, groupes b
  2. where a.article = b.article
  3. and b.pays = '01'
  4. and (a.article like '001%' or a.article like 'Z%')



 
Ca change tout (0.01 sec) ! :D
 
Merci  :jap:  
 
C'est la jointure qui semble poser problème, car le même type de requête sur la seule table articles est aussi rapide

Reply

Marsh Posté le 16-11-2007 à 11:46:00    

@anapajari : merci pour le conseil, c'est vrai que n'ai pas l'habitude d'écrire les jointures de cette façon, par souci de rapidité ...
 
:jap:
 
Quand au like '%' : c'est un varchar(13), et l'utilisation du LIKE est assez aisée (voire obligée) avec ce champ ...

Reply

Marsh Posté le 16-11-2007 à 15:45:30    

Mosca > C'est pas trop le problème de la jointure le souci (normalement l'optimiseur se débrouille), mais ton "or". Un "or" doit toujours être entre parenthèses, sinon il s'applique quelque soient les "and" autour :
 

Code :
  1. SELECT *
  2. FROM personne
  3. WHERE active = 'Y' AND age = 20 OR sexe = 'M'


 
Sélectionne "toutes les personnes actives agées de 20 ans" ET "toutes les personnes de sexe masculin, quelque soit leur age et qu'elles soient actives ou non".
=> En gros ton OR va te faire retourner des millions d'infos. Vu qu'en plus le tiens était sur un "like", ça empêchait l'utilsation des index sur "pays"... Autant dire tu foutais ton SGBD à genoux pour un résultat foireux :D

Reply

Sujets relatifs:

Leave a Replay

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