2010-08-27 2 views
4

J'essaie de «bin» tous mes clients en fonction du nombre d'articles achetés, et afficher les comptes de chaque bac. J'essaie de voir combien de personnes (account_id) ont acheté un article, combien ont acheté deux articles, tout le long de neuf articles, puis dix ou plus.mysql agrégat simple des résultats de la sous-requête est lente

Voici la requête que j'utilise - pour ce que ça vaut, je m'attendrais à ce que la requête effectue un scan complet de la table sur les ventes afin de générer les résultats, mais l'ensemble du processus prend une éternité!

Je viens d'un arrière-plan Oracle et j'ai écrit la requête comme je le ferais dans Oracle.

select thecnt 
     , count(*) 
    from (select count(*) 
       , case when count(*) >= 10 then 'tenormore' else cast(count(*) as char) end thecnt 
      from sales 
      where created >= SUBDATE(CURRENT_DATE(), INTERVAL 60 DAY) 
     group by account_id) sub 
group by thecnt 
order by thecnt; 

y a-t-il des pièges dans mysql quand il s'agit de sous-requêtes?

expliquer le plan

+----+-------------+-------------------+-------+---------------+---------+---------+------+---------+----------+-----------------------------------------------------------+ 
| id | select_type | table    | type | possible_keys | key  | key_len | ref |  rows | filtered | Extra              | 
+----+-------------+-------------------+-------+---------------+---------+---------+------+---------+----------+-----------------------------------------------------------+ 
| 1 | PRIMARY  | <derived2>  | ALL | NULL   | NULL | NULL | NULL | 2143248 | 100.00 | Using temporary; Using filesort       | 
| 2 | DERIVED  | sales    | range | created  | created | 8  | NULL | 2012492 | 100.00 | Using where; Using index; Using temporary; Using filesort | 
+----+-------------+-------------------+-------+---------------+---------+---------+------+---------+----------+-----------------------------------------------------------+ 
2 rows in set, 1 warning (1 hour 4 min 6.14 sec) 


mysql> describe sales; 
+-----------------+---------------------+------+-----+---------+-------+ 
| Field   | Type    | Null | Key | Default | Extra | 
+-----------------+---------------------+------+-----+---------+-------+ 
| account_id  | char(36)   | NO | PRI | NULL |  | 
| created   | datetime   | NO | MUL | NULL |  | 
| histogram_value | bigint(20) unsigned | NO | PRI | NULL |  | 
+-----------------+---------------------+------+-----+---------+-------+ 
+0

Pourriez-vous poster le résultat de 'EXPLAIN EXTENDED SELECT ...', s'il vous plaît Neil. –

Répondre

1

Vous manque probablement des indices appropriés.

EDIT:

Votre requête est lente parce que les subquerys resut Ne tenir dans la mémoire et une table temporaire sur le disque est utilisé.

Vous bénéficieriez d'index (account_id, créé) qui empêche l'aide de la table tmp sur le disque pour sous-requête, si elle est utilisée

ALTER TABLE sales ADD INDEX ix_acc_cre (account_id, created) 
+0

pourquoi voudrais-je ajouter un index si je prévois de balayer la table entière? Figure qu'il ya 70 jours de données dans le tableau et je suis intéressé par le plus récent 60. J'aurais dû être plus précis quand j'ai dit que je prévois sur un tableau complet. A moins que mysql n'utilise des chemins d'accès différents par rapport à Oracle ... –

+0

A l'heure actuelle, mysql doit faire beaucoup de tri et comme les index sont triés, cela peut être évité. Si vous insistez ** pour ne pas utiliser d'index, changez votre requête pour que 'from sales' devienne' from use use index() '- cela désactive effectivement l'utilisation des index –

1

Je ne vois rien de particulièrement mal avec votre requête. La raison pour laquelle la requête est lente est parce qu'elle doit utiliser des tables temporaires et filesort. La seule façon d'accélérer sérieusement cette requête sera de modifier vos paramètres MySQL pour allouer plus de mémoire, afin d'éviter d'utiliser le disque pour ces processus. Here's a spot on article covering the pertinent settings.


Edit: Une fois que vous faites cela, vous pouvez aussi économiser de la mémoire en spécifiant une colonne exacte de compter au lieu de COUNT(*), et quelques autres modifications mineures, comme certains des autres ont mentionné. Vous voulez obtenir un ensemble de données aussi petit que nécessaire pour tirer le meilleur parti de votre mémoire. Mais je pense que le problème global ne disparaîtra pas à moins d'allouer plus de mémoire.

+0

ok. Je vais changer ma requête pour compter le nombre de commandes au lieu de la ligne entière. J'ai besoin de regarder comment mysql optimise les requêtes de plus près. Avec un accès non privilégié, est-il possible de vérifier les paramètres de la mémoire via le dictionnaire de données de mysql?Je n'ai pas de compte shell sur la boîte. –

+0

Vous pouvez utiliser 'SHOW GLOBAL VARIABLES' pour afficher les paramètres actuels. – wuputah

0

Un index peut être utile dans une analyse de table complète si MySQL peut extraire les données de l'index au lieu de regarder les lignes réelles. Vous ne devriez pas avoir besoin de la sous-requête ici:

SELECT COUNT(account_id) AS thecnt, 
    IF(COUNT(account_id) < 10, COUNT(account_id), 'tenormore') 
    FROM sales 
    WHERE created >= SUBDATE(CURRENT_DATE(), INTERVAL 60 DAY) 
    GROUP BY account_id 
    ORDER BY thecnt DESC 

Espérons que cela aide.

+1

Si votre clé primaire commence par account_id (account_id, created, ...), l'ordre des disques sera proche de la fonction d'agrégation et le fichier n'aura pas grand chose à faire. Vous pouvez également envisager d'utiliser des déclencheurs pour maintenir un tableau récapitulatif à jour avec le nombre de commandes. –

+0

Sa requête externe d'origine est groupée par nombre. Il répond à la question «Combien de clients ont commandé (1, 2, ..., 10 ou plus) des articles au cours des 60 derniers jours? Ceci est différent de "Combien d'articles chaque client a-t-il commandé au cours des 60 derniers jours?" qui est ce que renvoie la requête interne (et votre requête). – wuputah

+0

Wuputah a exactement raison - j'essaie de regrouper par les comptes pour construire l'histogramme. Merci, cependant, d'avoir tiré une balle sur la question! –

Questions connexes