2010-05-18 6 views
2

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

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

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 hodls пользователей и 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: alt text http://img685.imageshack.us/img685/9440/explain.png

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

ОБНОВЛЕНИЕ Это мой запрос, основанный на ответе 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 

Вот ResultSet: alt text http://img714.imageshack.us/img714/2954/resultu.png

+0

все будет в порядке. но попытайтесь оценить эти данные в Postgres, проверьте, как это будет стоить – Hao

+0

да, это не может быть и речи! –

ответ

1

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

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

извините, но эти два запроса не дают результатов, которые я бы хотел. Первый возвращает только первое видео, которое находится в таблице «видео».Второй возвращает 8 видео, каждый из которых имеет tmp_num = 0. –

+0

@janh: исправлено, извините. – Quassnoi

+0

Теперь я отправляюсь домой, но я сделал быструю проверку, и, похоже, это дает те же результаты, что и мой запрос, только намного быстрее. Завтра заглянем в нее. Спасибо, сейчас! –

2

Да, ORDER BY на вычисляемый столбец всегда будет необратим. Сожалею.

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

+2

+1 - просто невозможно сделать это эффективным. Трюк заключается в том, чтобы избавиться от аспекта в реальном времени, регулярно пересчитывая их. Как еженедельно, ежедневно, даже ежечасно. Группируйте по расчетному столбцу => Убийца по умолчанию для определения. – TomTom