2009-11-08 7 views
1

est ici la requête:Comment puis-je optimiser cette requête SQL pour se débarrasser de la table filesort et temp?

SELECT 
    count(id) AS count 
FROM `numbers` 
GROUP BY 
    MONTH(created_at), 
    YEAR(created_at) 
ORDER BY 
    YEAR(created_at), 
    MONTH(created_at) 

Cette requête lance une « aide temporaire » et « Utilisation filesort » en faisant EXPLIQUEZ.

En fin de compte ce que je fais est à la recherche d'une table de numéros de suivi soumis par l'utilisateur et en comptant le nombre de lignes soumises un regroupement des comptes par mois/année.

ie. En novembre 2008, il y avait 11 312 lignes soumises.

MISE À JOUR, voici la description du tableau numbers.

id int(11) NO PRI NULL auto_increment 
tracking varchar(255) YES  NULL  
service varchar(255) YES  NULL  
notes text YES  NULL  
user_id int(11) YES  NULL  
active tinyint(1) YES  1 
deleted tinyint(1) YES  0 
feed text YES  NULL  
status varchar(255) YES  NULL  
created_at datetime YES  NULL  
updated_at datetime YES  NULL  
scheduled_delivery date YES  NULL  
carrier_service varchar(255) YES  NULL  
+0

Pourquoi voulez-vous vous en débarrasser? –

+1

poster le 'DESCRIBE TABLE' pour' numbers' –

+0

En fin de compte j'essaye de rendre la requête plus rapide. En ce moment, il interroge ~ 200 000 lignes et prend ~ 500ms. À mesure que le nombre de rangées augmente, cela prendra évidemment beaucoup plus de temps. – Shpigford

Répondre

0
SELECT 
    count(`id`) AS count, MONTH(`created_at`) as month, YEAR(`created_at`) as year 
FROM `numbers` 
GROUP BY month, year 
ORDER BY created_at 

Ce sera le meilleur que vous pouvez obtenir, pour autant que je peux dire. J'ai créé une table avec un id et une colonne datetime et rempli avec 10000 lignes. La requête ci-dessus utilise un sous-select, mais il ne vous fait pas vraiment différent et a le surcoût d'un sous-select. Le temps résultant pour le mien était 0.015s et le sien était 0.016s.

Assurez-vous que vous avez un index sur created_at, cela aidera votre requête initiale sur. Il est assez rare de ne pas se retrouver avec un tri de fichiers lorsque le groupe arrive, mais cela peut être possible dans d'autres situations. Les documents de MySql ont an article à ce sujet si vous vous sentez si enclin. Je ne vois pas comment ces méthodes peuvent être appliquées ici, avec les informations que vous avez fournies.

+0

Kevin, j'ai mis à jour le post avec le DESCRIBE. Merci! – Shpigford

1

Donnez ce un coup:

SELECT COUNT(x.id) 
    FROM (SELECT t.id, 
       MONTH(t.created_at) 'created_month', 
       YEAR(t.created_at) 'created_year' 
      FROM NUMBERS t) x 
GROUP BY x.created_month, x.created_year 
ORDER BY x.created_month, x.created_year 

Ce n'est pas une bonne habitude d'utiliser des fonctions dans les clauses WHERE, GROUP BY et ORDER BY car les index ne peuvent pas être utilisés.

... query lance un 'Using temporary' et 'Using filesort' en faisant EXPLAIN.

De ce que je found, à prévoir lors de l'utilisation DISTINCT/GROUP BY.

+0

Je suppose que vous avez fait la sous-requête simplement pour réduire le nombre de colonnes dans le résultat à compter? –

0

Assurez-vous que vous avez un indice de couverture sur année et le mois (qui est, les deux champs dans le même indice) afin que l'ordre par le composant de votre requête peut utiliser un index. Cela devrait supprimer le besoin d'un fichier, même si une table temporaire peut toujours être nécessaire pour gérer le regroupement.

0

Chaque fois que MySQL doit travailler en mémoire, et que le travail dépasse la quantité disponible (innodb_buffer_pool_size), il commence à utiliser le disque pour stocker le travail temporaire. Vous pourriez augmenter la variable que j'ai mentionnée, mais la régler trop haut pourrait causer des problèmes de performances dans d'autres domaines.

Si vous utilisez un serveur dédié, définissez à ~ 50-75%.

0

La meilleure méthode serait de créer une colonne d'aide qui contiendrait des valeurs Numberic de YEAR et MONTH concaténés:

YEAR(created_at) * 100 + MONTH(created_at)

Regrouper sur cette colonne utiliserait INDEX FOR GROUP BY.

Cependant, vous pouvez créer deux tables d'aide, le premier contenant nombre raisonnable d'années (disons, 1900-2100), le second contenant mois (de 0 à 11), et d'utiliser ces tables pour générer les ensembles :

SELECT (
     SELECT COUNT(*) 
     FROM numbers 
     WHERE created_at >= '1900-01-01' + INTERVAL y YEAR + INTERVAL m MONTH 
       AND created_at < '1900-01-01' + INTERVAL y YEAR + INTERVAL m + 1 MONTH 
     ) 
FROM year_table 
CROSS JOIN 
     month_table 
WHERE y BETWEEN 2008 AND 2010 
0

Je suis désolé, mais je ne suis pas d'accord avec les autres réponses. Je pense que ce dont vous avez besoin est d'ajouter un index à votre table, de préférence covering index.

Si vous ajoutez un index sur les colonnes que vous recherchez (created_at) et également sur les colonnes que vous voulez obtenir un résultat de (id), il sera alors considérablement plus rapide qu'avant.

La raison pour laquelle vous utilisez une table temporaire est que vous utilisez un groupe.
Pour accélérer le groupe, vous pouvez modifier les paramètres du serveur MySQL pour augmenter la taille de la table tmp et la taille maximale de la table de tas pour que la table temporaire soit en mémoire.

Questions connexes