2013-09-25 5 views
0

J'essaie de trouver des résultats d'examen pour les individuels entre plusieurs périodes en utilisant cette requête:Les mauvaises performances avec entre la requête

SELECT * FROM RESULTS AS R, Define_Times AS T 
WHERE R.PERSONID = T.PERSONID AND ( 
(R.DATE BETWEEN T.Previous_Month_Start AND T.Previous_Month_End) OR 
(R.DATE BETWEEN T.Next_Month_Start AND T.Next_Month_End) OR 
(R.DATE BETWEEN T.Six_Month_Start AND T.Six_Month_End) OR 
(R.DATE BETWEEN T.One_Year_Start AND T.One_Year_End) OR 
(R.DATE BETWEEN T.Two_Year_Start AND T.Two_Year_End) OR 
(R.DATE BETWEEN T.Three_Year_Start AND T.Three_Year_End) OR 
(R.DATE BETWEEN T.Four_Year_Start AND T.Four_Year_End)) 

Précédent/Suivant/One_Year etc. est différent pour chaque personne.

donne Expliquer:

| id | select_type | table | type | possible_keys | key | key_len | ref    | rows | Extra  | 
| 1 | SIMPLE  | T  | ALL | PEOPLE  | NULL | NULL | NULL   | 75775 |    | 
| 1 | SIMPLE  | R  | ref | IDX3,IDX2  | IDX3 | 5  | T.PERSONID  | 3550 | Using where | 

Le tableau de résultats a environ 300 millions de lignes. Define_Times a 75 000.

Ça prend de l'AGES.

Je vois que le 1er type est ALL, ce qui est mauvais. Mais si c'est si mauvais, pourquoi n'utilise-t-il pas l'index sur PERSONID (appelé PEOPLE) identifié comme possible? Que puis-je faire pour améliorer cela?

Je ne peux pas non plus le voir en utilisant un index pour la date - il y en a un sur R.DATE. (C'est le premier dans la séquence de 5 sur l'index appelé IDX2.)

Désolé pour toutes les fautes de frappe - mon clavier est cassé, et merci d'avance.

+0

Avez-vous essayé de sélectionner les deux tables en utilisant 'join' à la place? – Amber

+2

Cela utilise une jointure, juste une sémantique différente. –

+0

Est-ce que chaque ligne de Define_Times a une personne correspondante dans Results? –

Répondre

0

A titre de comparaison, pouvez-vous exécuter cette requête équivalente

SELECT * FROM Define_Times AS T 
INNER JOIN RESULTS AS R on 
(R.PERSONID = T.PERSONID and 
    ( 
    (R.DATE BETWEEN T.Previous_Month_Start AND T.Previous_Month_End) OR 
    (R.DATE BETWEEN T.Next_Month_Start AND T.Next_Month_End) OR 
    (R.DATE BETWEEN T.Six_Month_Start AND T.Six_Month_End) OR 
    (R.DATE BETWEEN T.One_Year_Start AND T.One_Year_End) OR 
    (R.DATE BETWEEN T.Two_Year_Start AND T.Two_Year_End) OR 
    (R.DATE BETWEEN T.Three_Year_Start AND T.Three_Year_End) OR 
    (R.DATE BETWEEN T.Four_Year_Start AND T.Four_Year_End) 
) 
) 

J'ai vu beaucoup mieux parfois le travail d'optimisation sous cette forme. En outre, puisque vous OU toute la date entre les expressions, il n'a pratiquement aucun moyen d'utiliser un index de date, puisque l'une des plages de dates peut satisfaire à la clause where.

EDIT - AJOUTS

Si vous ne voulez pas exécuter la requête, au moins essayez de comparer l'exécution des plans estimé

+0

Donc, fondamentalement, changer le WHERE à INNER JOIN? (Juste pour confirmer que je n'ai rien manqué.) Je peux l'essayer - mais si ça ne s'améliore pas, nous ne le saurons pas avant une semaine! ;-) –

+0

Juste vu votre edit - par le plan d'exécution, voulez-vous dire EXPLAIN? Si oui, exactement pareil. –

+0

Le plan estimé signifie que la requête n'est pas réellement exécutée, mais qu'elle fournit une «meilleure estimation» du plan d'exécution réel. Vous voudrez peut-être faire cela pour interroger les alternatives qui sont en cours d'exécution que vous préférez ne pas exécuter sur la base de données. –

2

Le problème est que toutes les conditions que vous avez ORED ensemble.

Si possible, restructurer votre base de données afin que Define_Time a quatre colonnes:

CREATE TABLE Define_Times (
    PersonID INTEGER, 
    PeriodType SomeType, 
    StartDate DATE, 
    EndDate DATE) 

Ensuite, chaque personne reçoit 7 dossiers (ou plus, s'il y a plus de périodes que vous ne recherchez pas dans votre exemple) dans lequel PeriodType indique quelle période les dates spécifient (vous pouvez utiliser des valeurs de texte telles que PM, NM, SM, 1Y, 2Y, 3Y, 4Y ou vous pouvez utiliser des valeurs entières pointant vers une description dans une autre table).

Ensuite, réécrire votre requête comme ceci:

SELECT * FROM RESULTS AS R, Define_Times AS T 
WHERE R.PERSONID = T.PERSONID 
    AND R.DATE BETWEEN T.StartDate AND T.EndDate 
    AND T.PeriodType IN (PM,NM,SM,1Y,2Y,3Y,4Y) 

Cette requête est au moins optimisable.

Cette requête produira un enregistrement par période appariée pour chaque personne. Si vos périodes ne se chevauchent pas, c'est bien (il n'y aura jamais qu'un seul enregistrement correspondant). Si vos périodes se chevauchent et que vous ne voulez qu'un enregistrement par ensemble de résultats, vous devrez effectuer un travail supplémentaire avec DISTINCT ou GROUP BY en regroupant les enregistrements dans le jeu de résultats.

Notez également que si vous ne faites pas ont des périodes supplémentaires dans la table Define_Times alors vous pouvez enlever la partie AND T.PeriodType de la clause WHERE.

+0

Cool - Je vais essayer et vous le faire savoir. Merci! Aurais-je besoin d'un index spécifique pour tirer parti de cette nouvelle requête? –

+1

Un index commençant par (PersonID, Date) sur la table Les résultats seraient le meilleur moyen d'optimiser. Je ne suis pas un expert MySQL et je ne sais pas comment il va optimiser cette requête, mais au moins il a la possibilité de faire une optimisation. –

Questions connexes