2010-08-27 2 views
4

Я пытаюсь «выложить» всех своих клиентов на основе количества купленных предметов и отображать подсчеты каждого бункера. Я пытаюсь понять, сколько человек (account_id) купило один элемент, сколько купленных двух предметов, всего через девять элементов, а затем десять или больше.mysql простой агрегат результатов подзапроса медленный

Вот запрос, который я использую - для чего стоит, я бы ожидал, что запрос будет выполнять сканирование полной таблицы при продажах, чтобы генерировать результаты, но весь процесс берет навсегда!

Я исхожу из фона Oracle, и я написал запрос, как в 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; 

Есть ли какие-либо проблемы в mysql при работе с подзапросами?

объяснить план

+----+-------------+-------------------+-------+---------------+---------+---------+------+---------+----------+-----------------------------------------------------------+ 
| 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

Не могли бы вы опубликовать результат «EXPLAIN EXTENDED SELECT ...», пожалуйста, Нейл. –

ответ

1

Вы, вероятно, отсутствуют соответствующие индексы.

EDIT:

Ваш запрос медленно, потому что subquerys resut Dont помещается в память и временную таблицу на диске используется.

Таким образом, вы бы извлечь выгоду из индекса на (ACCOUNT_ID, созданный), который предотвращает его с помощью ТМП таблицы на диске для подзапроса, если используется

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

Почему я хочу добавить индекс, если я планирую сканировать всю таблицу? Рисунок, что в таблице есть данные за 70 дней, и меня интересуют последние 60. Я должен был быть более конкретным, когда я сказал, что планирую полноэкранное сканирование. Если mysql использует разные пути доступа по сравнению с oracle ... –

+0

Прямо сейчас mysql должен выполнять большую сортировку, и поскольку индексы отсортированы, этого можно избежать. Если вы ** настаиваете ** на том, чтобы не использовать индексы, измените запрос так, чтобы 'from sales' стал' из индекса использования продаж() '- это фактически отключает использование любых индексов –

1

Я не вижу ничего особенно плохого в вашем запросе. Причина медленного запроса заключается в том, что он должен использовать временные таблицы и filesort. Единственный способ серьезно ускорить этот запрос - изменить ваши настройки MySQL, чтобы выделить больше памяти, чтобы избежать использования диска для этих процессов. Here's a spot on article covering the pertinent settings.


Edit: После того, как вы сделаете это, вы также можете сохранить память, указав точный столбец сосчитать вместо COUNT(*), и несколько других незначительных настроек, так как некоторые из других упоминали. Вы хотите получить как можно меньше данных, чтобы максимально использовать свою память. Но я думаю, что общая проблема не исчезнет, ​​если вы не выделите больше памяти.

+0

ok. Я поменяю свой запрос, чтобы подсчитать количество заказов вместо целой строки. Мне нужно посмотреть, как mysql оптимизирует запросы немного ближе. С не-привилегированным доступом, есть ли способ проверить настройки памяти через словарь данных mysql?У меня нет учетной записи оболочки на коробке. –

+0

Вы можете использовать 'SHOW GLOBAL VARIABLES' для просмотра текущих настроек. – wuputah

0

Индекс может быть полезен при полной проверке таблицы, если MySQL может извлечь данные из индекса вместо того, чтобы смотреть на фактические строки. Вам не нужен подзапрос здесь:

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 

Надеюсь, это поможет.

+1

Если ваш первичный ключ начинается с account_id (account_id, created, ...), то порядок хранения данных на диске будет близок к агрегированной функции, и файловому контуру будет не так много. Вы также можете использовать триггеры, чтобы обновлять сводную таблицу с учетом количества заказов. –

+0

Его исходный внешний запрос группируется по счету. Он отвечает на бизнес-вопрос: «Сколько клиентов заказали (1, 2, ..., 10 или более) товары за последние 60 дней?» Это отличается от «Сколько элементов выполнял каждый заказ клиента за последние 60 дней?» который возвращает внутренний запрос (и ваш запрос). – wuputah

+0

Wuputah точно прав - я пытаюсь сгруппировать по подсчетам, чтобы построить гистограмму. Спасибо, однако, за то, что вы взяли на себя вопрос! –