2010-05-18 111 views
2

我有一個網頁,用戶上傳&觀看視頻。上週我asked跟蹤視頻觀看的最佳方式是什麼,以便我可以顯示本週觀看次數最多的視頻(所有日期的視頻)。優化GROUP BY&ORDER BY查詢

現在我需要一些幫助來優化查詢,我從中獲取數據庫中的視頻。相關表是這樣的:

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

那麼,我該如何優化呢?

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 

下面是結果集: alt text http://img714.imageshack.us/img714/2954/resultu.png

+0

這是不可能的。但嘗試評估Postgres上的數據,檢查它將如何票價 – Hao 2010-05-18 12:49:56

+0

是的,這是不可能的:) – 2010-05-18 14:56:15

回答

1

創建以下指標:

video_views (views_date, videos_id) 

,並獲得videosviews(它不與您當前的查詢工作之間擺脫LEFT JOIN的,無論如何):

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. – 2010-05-18 12:16:03

+0

@janh:已更正,對不起。 – Quassnoi 2010-05-18 12:34:13

+0

我現在回家了,但我做了一個快速檢查,看起來這與我的查詢產生的結果相同,只是速度要快得多。明天將考慮它。現在感謝! – 2010-05-18 14:56:00

2
對計算

呀,ORDER BY列始終是不可索引的。抱歉。

如果您要做很​​多查詢,並且您希望避免每次必須對每個視頻進行計數和排序的視圖,則必須進行非規範化處理。添加一個views_in_last_week列,每天從後臺重新計算videos_views,並將其索引(可能位於具有其他相關WHERE條件的複合索引中)。

+2

+1 - 根本無法提高效率。訣竅是通過定期重新計算來擺脫實時性方面的問題。就像每週,每天甚至每小時一樣。按計算列分組=>每個定義的Pewrformance殺手。 – TomTom 2010-05-18 10:52:35