Vous utilisez la condition de plage sur la première colonne d'index qui empêche la possibilité de filtrer sur d'autres colonnes.
Il n'existe aucune plage contiguë dans cet index qui contiendrait ces plages et uniquement celles qui satisfont la condition.
MySQL
ne peut pas faire SKIP SCAN
qui sauterait par-dessus les valeurs distinctes de di_date
. C'est pourquoi il fait de son mieux: utilise l'accès range
pour filtrer sur di_date
et utilise WHERE
pour filtrer sur tous les autres champs.
Soit recréer l'index comme cela (la meilleure décision):
PRIMARY KEY (`di_sid`,`di_type`,`di_name`,`di_date`,`di_abt`)
ou, si vous ne parvenez pas à recréer l'index, vous pouvez émuler le SKIP SCAN
:
SELECT MONTH(di.di_date) as label1, DAYOFMONTH(di.di_date) as label2, sum(di.di_num) as count , di.di_abt as abt
FROM (
SELECT DISTINCT di_date
FROM daily_info
WHERE di_date > '2009-10-01' AND di_date < '2009-10-16'
) do
JOIN daily_info di
ON di.di_date <= do.di_date
AND di.di_date>= do.di_date
AND di_sid = 6
AND di_type = 4
AND di_name = 'clk-1'
GROUP BY
DAYOFMONTH(di.di_date)
ORDER BY
TO_DAYS(di.di_date) DESC
Assurez-vous que Using index for group-by
et Range checked for each record
sont présents dans le plan.
Cette condition:
di.date <= do.date
AND di.date >= do.date
est utilisé au lieu de simples di.date = do.date
pour forcer la vérification de la plage.
Voir cet article dans mon blog pour une explication plus détaillée de émulant SKIP SCAN:
Mise à jour:
Cette dernière requête utilise en fait un équijointure et MySQL
Optimise sans les astuces.
L'astuce ci-dessus s'applique uniquement aux requêtes à distance, i. e. lorsque la boucle la plus interne doit utiliser l'accès range
, pas l'accès ref
.
Il serait utile si vous deviez faire quelque chose comme di_name <= 'clk-1'
Cette requête devrait fonctionner correctement:
SELECT MONTH(di.di_date) as label1, DAYOFMONTH(di.di_date) as label2, sum(di.di_num) as count , di.di_abt as abt
FROM (
SELECT DISTINCT di_date
FROM daily_info
WHERE di_date > '2009-10-01' AND di_date < '2009-10-16'
) do
JOIN daily_info di
ON di.di_date = do.di_date
AND di_sid = 6
AND di_type = 4
AND di_name = 'clk-1'
GROUP BY
DAYOFMONTH(di.di_date)
ORDER BY
TO_DAYS(di.di_date) DESC
Assurez-vous que di
utilise l'accès ref
sur la sous-clé tout possible ici, avec key_len = 33
Mise à jour 2
Dans votre requête, vous utilisez ces expressions de la GROUP BY
:
MONTH(di_date)
TO_DAYS(di_date)
di_abt
La requête comme il est maintenant résumera toutes les valeurs pour le 1st
, etc. 2nd
pour un mois et l'année.
I. e. pour le premier groupe, il ajoutera toutes les valeurs de Jan 1st, 2000
, puis Feb 1st, 2000
, etc.
Ensuite, il retournera toute valeur aléatoire de MONTH
, une valeur aléatoire de TO_DAYS
et une valeur aléatoire de di_abt
de chaque groupe.
Votre condition est maintenant dans un seul mois, donc c'est OK maintenant, mais si votre condition s'étendra sur plusieurs mois (sans parler des années), la requête produira des résultats inattendus.
Voulez-vous vraiment regrouper par dates?
Merci Quassnoi. – Nir
Inedeed J'ai fait la 1ère option et j'ai récupéré des fichiers. Je vais essayer l'option de sous-sélection. Devrais-je retourner l'index à l'original? – Nir
Vous obtiendrez un fichier de toute façon, il n'y a aucun moyen de s'en débarrasser dans cette requête exacte. Et je viens de remarquer une petite faille dans votre requête, voir la mise à jour post. – Quassnoi