MySQL запрос для первых N записей сгруппированных записей - PullRequest
3 голосов
/ 11 декабря 2010

Я новичок в MySql и базах данных в целом.У меня есть запрос, который я собрал с помощью фрагментов из онлайн-ресурсов, а также следов и ошибок.Это действительно медленно (27 секунд), и я предполагаю, что его можно оптимизировать.Может быть, кто-то может помочь мне с этим.

Это структура данных для моей базы данных MySQL.Версия 5.1.51-0

|- purchaseID -|- customerID -|- emotionID -|- customerCountryCode -|- customerContinentCode-|
|     1        |     2345     |     0       |        US             |            NA          |
|     2        |     2345     |     3       |        US             |            NA          |
|     3        |     4456     |     0       |        UK             |            EU          |
|     3        |     4456     |     5       |        UK             |            EU          |
|     4        |     4456     |     2       |        UK             |            EU          |
|     5        |     4456     |     2       |        UK             |            EU          |
|     6        |     1234     |     0       |        US             |            NA          |
|     7        |     6678     |     0       |        US             |            NA          |
|     8        |     9900     |     0       |        US             |            NA          |
|     9        |     3334     |     0       |        US             |            NA          |    
|     10       |     3334     |     4       |        US             |            NA          |

База данных используется для сохранения всех покупок, которые сделаны.Для каждой покупки customerID, страна и континент, из которого он прибывает, сохраняются.Клиент также имеет возможность оценить свою покупку из набора из 6 эмоций.(счастлив, разочарован, ...) Выбранные им эмоции сохраняются как emotionID.

Так что теперь мне нужен запрос, чтобы получить 6 лучших клиентов для определенного emotionID с процентной информацией.Предположим, я искал emotionID = 0 вот что я хотел бы получить:

|- customerID -|- emotionPercent -|
|     1234     |        100       |     
|     6678     |        100       |     
|     9900     |        100       | 
|     2345     |        50        |     
|     3334     |        50        | 
|     4456     |        25        |    

Я использую этот запрос:

SELECT customers.customerID, Count( customers.emotionID ) / C.totalPeople * 100.0 AS emotionPercent 
FROM `customers` 
INNER JOIN 

    (SELECT customers.customerID, Count( customers.emotionID ) AS totalPeople
    FROM `customers` 
    GROUP BY customerID) C 

ON customers.customerID = C.customerID 
WHERE customers.emotionID = 0 
GROUP BY customers.customerID 
ORDER BY emotionPercent DESC 
LIMIT 0,6

Я искал ответы, нодополнительный процентный расчет сбивает меня с толку.Я нашел несколько решений, которые потребовали бы заполнения какой-то временной таблицы, но я не смог заставить ее работать.

Проблема в том, что сейчас в базе данных 140 000 записей, и этот запрос занимаетоколо 27 секунд.Это может быть правильно?Будет ли использование SQL-сервера значительно увеличить скорость?

Чего я не получу, так это: запрос самой счастливой страны в мире молниеносен (0,4 секунды), но структурно аналогичен первому запросу (27 секунд):

SELECT customers.customerCountryCode, Count( customers.emotionID ) / C.totalPeople * 100.0 AS emotionPercent 
FROM `customers` 
INNER JOIN 

    (SELECT customers.customerCountryCode, Count( customers.emotionID ) AS totalPeople
    FROM `customers` 
    GROUP BY customerCountryCode) C 

ON customers.customerCountryCode = C.customerCountryCode 
WHERE customers.emotionID = 0 
GROUP BY customers.customerCountryCode 
ORDER BY emotionPercent DESC 
LIMIT 0,6

Когда я изменяю GROUP BY INNER Query в этом примере на customerID, запрос также выполняется вечно.Так что проблема заключается в группировке по customerID.Но почему?

customerCountryCode определяется как varchar(2).customerID - это int(11).Вызывает ли это огромную разницу в производительности запросов?Есть ли еще подходящий тип переменной?В customerID может быть до 8 номеров.

Много вопросов!Спасибо за чтение и любую помощь!

Ответы [ 3 ]

0 голосов
/ 13 декабря 2010

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

  1. Получите среднюю эмоцию для каждого клиента и выберите первые 6, сохраните в хэш илиobject.
  2. Получить этих 6 клиентов с помощью WHERE custumerID IN (id1, id2, id3, etc)

Хотя это, вероятно, не самое симпатичное решение, вы избегаете использовать подзапрос без индекса (и очень медленный полныйсканирование таблицы).

0 голосов
/ 14 декабря 2010

Спасибо за вашу помощь!

Ребята с форума mySQL предложили добавить несколько индексов:

ALTER TABLE customers
  ADD KEY idx_country_emid (customerCountryCode, emotionID),
  ADD KEY idx_emid_custid (emotionID, customerID);

Время запроса сократилось с 27 до 0,1 секунды. ;)

Также для внутреннего запроса вы можете написать

(SELECT customers.customerCountryCode, Count( * ) AS totalPeople
    FROM `customers` 
    GROUP BY customerCountryCode) C 
0 голосов
/ 11 декабря 2010

Во-первых, если вы думаете, что записи в вашей базе данных будут всплывающими, или если ваши записи высоки, а сервер работает медленно, ИМХО, вы хотели бы предварительно обработать данные и сохранить их в другой базе данных с помощьюТаким образом, вам не придется запрашивать один и тот же процесс снова и снова.Также попробуйте использовать плагины кэширования для вашего приложения.memcache для php или ehcache на j2ee - безопасные ставки.

...