Оптимизировать запрос GROUP BY & ORDER BY - PullRequest
2 голосов
/ 18 мая 2010

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

Теперь мне нужна помощь в оптимизации запроса, с помощью которого я получаю видео из базы данных. Соответствующие таблицы таковы:

video (~239371 rows)
VID(int), UID(int), title(varchar), status(enum), type(varchar), is_duplicate(enum), is_adult(enum), channel_id(tinyint)

signup (~115440 rows)
UID(int), username(varchar)

videos_views (~359202 rows after 6 days of collecting data, so this table will grow rapidly)
videos_id(int), views_date(date), num_of_views(int)

Таблица video содержит видео, signup содержит пользователей и videos_views содержит данные о просмотрах видео (каждое видео может иметь одну строку в день в этой таблице).

У меня есть этот запрос, который справляется с задачей, но для его выполнения требуется ~ 10 секунд, и я думаю, что со временем он будет только ухудшаться по мере увеличения таблицы videos_views.

SELECT
 v.VID, 
 v.title, 
 v.vkey, 
 v.duration, 
 v.addtime, 
 v.UID, 
 v.viewnumber, 
 v.com_num, 
 v.rate, 
 v.THB, 
 s.username,
 SUM(vvt.num_of_views) AS tmp_num
FROM
 video v
  LEFT JOIN videos_views vvt ON v.VID = vvt.videos_id
  LEFT JOIN signup s on v.UID = s.UID
WHERE
 v.status = 'Converted'
 AND v.type = 'public'
 AND v.is_duplicate = '0'
 AND v.is_adult = '0'
 AND v.channel_id <> 10
 AND vvt.views_date >= '2001-05-11'
GROUP BY
 vvt.videos_id
ORDER BY
 tmp_num DESC
LIMIT
 8

Все соответствующие поля проиндексированы. И вот скриншот результата EXPLAIN: альтернативный текст http://img685.imageshack.us/img685/9440/explain.png

Итак, как я могу оптимизировать это?

UPDATE Это мой запрос, основанный на ответе Quassnoi. Он возвращает правильные видео, но он портит JOIN в таблице регистрации. Для некоторых записей поле username имеет значение NULL, для других оно содержит неправильное имя пользователя.

SELECT
    v.VID,
    v.title,
    v.vkey,
    v.duration,
    v.addtime,
    v.UID,
    v.viewnumber,
    v.com_num,
    v.rate,
    v.THB,
    s.username
FROM
    (SELECT
        videos_id,
        SUM(num_of_views) AS tmp_num
    FROM
        videos_views
    WHERE
        views_date >= '2010-05-13'
    GROUP BY
        videos_id
    ) q
        JOIN video v ON v.VID = q.videos_id
        LEFT JOIN signup s ON s.UID = v.VID
WHERE
    v.type = 'public'
    AND v.channel_id <> 10
    AND v.is_adult = '0'
    AND is_duplicate = '0'
ORDER BY
    tmp_num DESC
LIMIT
    8

Вот результат: альтернативный текст http://img714.imageshack.us/img714/2954/resultu.png

Ответы [ 2 ]

2 голосов
/ 18 мая 2010

Да, ORDER BY в вычисляемом столбце всегда будет неиндексируемым.Извините.

Если вы собираетесь многократно выполнять этот запрос и хотите избежать подсчета и упорядочивания просмотров каждого видео каждый раз, вам придется денормализовать.Добавьте столбец views_in_last_week, пересчитывайте его из videos_views в фоновом режиме каждый день и индексируйте его (возможно, в составном индексе с другими соответствующими условиями WHERE).

1 голос
/ 18 мая 2010

Создайте следующий индекс:

video_views (views_date, videos_id)

и избавьтесь от LEFT JOIN между videos и views (в любом случае он не работает с вашим текущим запросом):

SELECT  *
FROM    (
        SELECT  videos_id, SUM(num_of_views) AS tmp_num
        FROM    video_views
        GROUP BY
                videos_id
        ) q
JOIN    videos v
ON      v.vid = q.videos_id
LEFT JOIN
        signup s
ON      s.UID = v.UID
ORDER BY
        tmp_num DESC
LIMIT 8

Если вы хотите вернуть ноль для видео, которые никогда не просматривались, измените порядок полей в индексе:

video_views (videos_id, views_date)

и перепишите запрос:

SELECT  *,
        (
        SELECT  COALESCE(SUM(num_of_views), 0)
        FROM    video_views vw
        WHERE   vw.videos_id = v.vid
                AND views_date >= '2001-05-11'
        ) AS tmp_num
FROM    videos v
LEFT JOIN
        signup s
ON      s.UID = v.UID
ORDER BY
        tmp_num DESC
LIMIT 8
...