2012-11-15 5 views
0

J'ai une requête SQL (mySQL 5.1.51) qui fait PHP timeout. Je voudrais l'optimiser mais je ne trouve pas ce qui manque. La demande est:Optimiser la requête mysql (utiliser ou créer un index)

SELECT s_i.incident, 
     s.hostname, 
     a.application, 
     s_ie.problem_status, 
     s_i.open_time, 
     s_i.close_time, 
     s_ie.open_group, 
     s_ie.primary_assignment, 
     s_ie.closed_by_group, 
     s_ie.contact_first_name, 
     s_ie.contact_last_name, 
     s_ie.description, 
     s_ie.resolution, 
     s_ie.famille_1, 
     s_ie.famille_2, 
     s_ie.famille_3, 
     YEARWEEK(s_i.open_time)    AS 'semaine_ouverture', 
     DATE_FORMAT(s_i.open_time, '%Y-%m') AS 'mois_ouverture', 
     YEARWEEK(s_i.close_time)    AS 'semaine_cloture', 
     DATE_FORMAT(s_i.close_time, '%Y-%m') AS 'mois_cloture', 
     p.nom, 
     s.exploite_par, 
     t.environnement, 
     a.tdb 
FROM t_link_serveur_eac t USE KEY(nna) 
    INNER JOIN serveur   s ON s.id   = t.id_serveur 
    INNER JOIN plateau   p ON p.id   = t.id_plateau 
    INNER JOIN applications  a ON a.nna   = t.nna 
    INNER JOIN scope_i   s_i USE KEY (id_serveur) ON s_i.id_serveur = t.id_serveur 
    INNER JOIN scope_i_extended s_ie USE KEY (id_scope_i) ON s_ie.id_scope_i = s_i.id 
WHERE s_ie.problem_status  = 'Closed' 
AND s_ie.contact_first_name = 'AUTOMATE' 
AND s_ie.contact_last_name LIKE '%BEM%' 
AND p.id   = 4 
AND open_time >= CURDATE() - INTERVAL 52 WEEK AND open_time <= CURDATE() 
AND AND s_i.close_time < CURDATE() - INTERVAL DAYOFMONTH(CURDATE()) - 1 DAY 
ORDER BY mois_cloture 

Quand je demande mySQL de l'expliquer, j'ai un type de ligne « ALL » pour l'union de la table s_ie. J'ai essayé de créer/modifier tous les index possibles, mais tous mes essayé n'a fait aucune différence:

id select_type table type possible_keys key  key_len ref     rows Extra 
1 SIMPLE  p  const PRIMARY   PRIMARY 4  const     1  Using temporary; Using filesort 
1 SIMPLE  a  ALL  PRIMARY   NULL  NULL  NULL     957 
1 SIMPLE  t  ref  nna    nna  26  inspire.a.nna   10 Using where 
1 SIMPLE  s  eq_ref PRIMARY   PRIMARY 4  inspire.t.id_serveur 1 
1 SIMPLE  s_i  ref  id_serveur  id_serveur 4  inspire.t.id_serveur 135 Using where 
1 SIMPLE  s_ie eq_ref id_scope_i  id_scope_i 4  inspire.s_i.id  1  Using where 

s_ie a 712.000 lignes et S_I 740,000 donc je pense que le problème vient de cette jonction

ici est la structure de la table s_ie

CREATE TABLE IF NOT EXISTS `scope_i_extended` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `id_scope_i` int(11) NOT NULL, 
    `problem_status` varchar(16) NOT NULL, 
    `open_group` varchar(32) NOT NULL, 
    `primary_assignment` varchar(32) NOT NULL, 
    `closed_by_group` varchar(32) NOT NULL, 
    `contact_first_name` varchar(32) NOT NULL, 
    `contact_last_name` varchar(32) NOT NULL, 
    `description` text NOT NULL, 
    `resolution` text NOT NULL, 
    `famille_1` text NOT NULL, 
    `famille_2` text NOT NULL, 
    `famille_3` text NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `id_scope_i` (`id_scope_i`), 
    UNIQUE KEY `problem_status` (`id_scope_i`, `problem_status`, `contact_first_name`, `contact_last_name`), 
    KEY `contact_last_name` (`contact_last_name`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

Et la structure de S_I

CREATE TABLE IF NOT EXISTS `scope_i` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `incident` varchar(20) NOT NULL, 
    `statut` varchar(20) NOT NULL, 
    `id_serveur` int(11) NOT NULL, 
    `open_time` datetime NOT NULL, 
    `close_time` datetime DEFAULT NULL, 
    `affectation` varchar(50) NOT NULL, 
    `titre` varchar(200) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `incident` (`incident`), 
    KEY `serveur` (`id_serveur`), 
    KEY `serveur_open_time` (`id_serveur`,`open_time`), 
    KEY `id_serveur` (`id_serveur`,`close_time`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=738862 ; 

Pourriez-vous m'aider/me sauver?

Cordialement,

Olivier

+1

après le plan d'exécution de la requête et s'il vous plaît –

+0

simplement modifier la question et le mettre là, ne pas essayer d'ajouter la sortie dans les commentaires –

+0

En effet, les commentaires ne peuvent pas être formatés sur faire un plan d'exécution correctement lisible. J'ai édité la question et l'ai ajoutée. –

Répondre

2

Désolé pour lapalissade mais je suggère d'utiliser:
"YEARWEEK(open_time) <= '201246' AND YEARWEEK(open_time) >= '201146'"

au lieu de
"YEARWEEK(open_time) IN (...)"

IN ralentit toujours les choses de façon spectaculaire.

+2

En effet, c'est un point que j'ai vu et je vais demander au développeur d'optimiser cette partie aussi (waybe avec BETWEEN aussi) –

+0

@Olivier Gérault Vous pouvez jeter ce lien au dev: http: // use-the- index-luke.com/sql/where-clause/obfuscation/dates –

+0

Merci, je l'ai lu et appliqué les suggestions mais toujours l'exécution échoue en PHP à cause du timeout. –

1

Dans votre condition, vous avez des colonnes de date enveloppées dans une fonction Mysql. ex: YEARWEEK(open_time) et DATE_FORMAT(s_i.close_time, '%Y-%m-%d') Vous devriez éviter cela car Mysql semble exécuter la fonction pour chaque ligne de la table.

Pouvez-vous essayer de remplacer

DATE_FORMAT(s_i.close_time, '%Y-%m-%d') < DATE_FORMAT(NOW(), '%Y-%m-01')

par

s_i.close_time < CURDATE() - INTERVAL DAYOFMONTH(CURDATE()) - 1 DAY

et

YEARWEEK(open_time) IN ('201246', '201245'....)

par ceci: (ci-dessous est la condition de ge t tous les enregistrements ont "open_time" dans l'année. Je ne sais pas si c'est votre cas)

open_time >= CURDATE() - INTERVAL 1 YEAR AND open_time <= CURDATE()

+0

Merci pour vos suggestions. Je les ai appliquées et cela me fait gagner du temps d'exécution (1.5s vs 1.08s). Néanmoins, il échoue toujours. J'ai oublié de dire que le résultat est un tableau de 31.000 lignes –