2011-12-12 112 views
0

我的CMS系統中有一個模塊,允許網站顯示廣告。它記錄視圖和點擊。我用來總結日誌的查詢表現不佳。MySQL:日誌摘要查詢

這是查詢:

SELECT `a`.`id`, 
    `a`.`active`, 
    `a`.`static`, 
    `a`.`position`, 
    `a`.`file`, 
    `a`.`title`, 
    `a`.`url`, 
    COUNT(DISTINCT `lv`.`id`) AS `views`, 
    COUNT(DISTINCT `lc`.`id`) AS `clicks` 
FROM `ads` AS `a` 
LEFT JOIN `ad_log` AS `lv` 
    ON `lv`.`ad_id` = `a`.`id` 
    AND `lv`.`type` = 'view' 
    AND `lv`.`created` BETWEEN '2011-01-01 00:00:00' 
     AND '2011-12-31 23:59:59' 
LEFT JOIN `ad_log` AS `lc` 
    ON `lc`.`ad_id` = `a`.`id` 
    AND `lc`.`type` = 'click' 
    AND `lc`.`created` BETWEEN '2011-01-01 00:00:00' 
     AND '2011-12-31 23:59:59' 
GROUP BY `a`.`id` 
ORDER BY `a`.`static` DESC, 
    `a`.`position` ASC, 
    `a`.`title` ASC 

ad_log表對ad_idtype列的兩列的索引。當我查看分析器結果時,它使用該索引。一個不同的索引會更高效嗎?


UPDATE

測試不同的指數組合後,似乎目前的一個是最好的。也許有更好的方法來編寫查詢?

這裏是EXPLAIN SELECT SQL_NO_CACHE ...抓屏:

EXPLAIN SELECT SQL_NO_CACHE ...


SOLUTION

我已經接受DRapp's solution,但這裏是我會拿出查詢。這只是略低於高性能比DRapp's solution

SELECT `a`.`id`, 
    `a`.`active`, 
    `a`.`static`, 
    `a`.`position`, 
    `a`.`file`, 
    `a`.`title`, 
    `a`.`url`, 
    (SELECT COUNT(*) 
     FROM `ad_log` 
     WHERE `ad_id` = `a`.`id` 
     AND `type` = 'view' 
     AND `created` BETWEEN '2011-11-01 00:00:00' 
      AND '2011-11-30 23:59:59') AS `views`, 
    (SELECT COUNT(*) 
     FROM `ad_log` 
     WHERE `ad_id` = `a`.`id` 
     AND `type` = 'click' 
     AND `created` BETWEEN '2011-11-01 00:00:00' 
      AND '2011-11-30 23:59:59') AS `clicks` 
FROM `ads` AS `a` 
ORDER BY `a`.`static` DESC, 
    `a`.`position` ASC, 
    `a`.`title` ASC 

最好的性能

這個查詢,通過DRapp's solution啓發,具有更好的性能:

SELECT `a`.`id`, 
    `a`.`active`, 
    `a`.`static`, 
    `a`.`position`, 
    `a`.`file`, 
    `a`.`title`, 
    `a`.`url`, 
    SUM(CASE WHEN `l`.`type` = 'view' THEN 1 ELSE 0 END) AS `views`, 
    SUM(CASE WHEN `l`.`type` = 'click' THEN 1 ELSE 0 END) AS `clicks` 
FROM `ads` AS `a` 
LEFT JOIN `ad_log` AS `l` 
    ON `a`.`id` = `l`.`ad_id` 
    AND `l`.`created` BETWEEN '2011-11-01 00:00:00' 
     AND '2011-11-30 23:59:59' 
GROUP BY `a`.`id` 
ORDER BY `a`.`static` DESC, 
    `a`.`position` ASC, 
    `a`.`title` ASC 
+0

出於興趣也可以張貼EXPLAIN SELECT的輸出SQL_NO_CACHE .....其餘查詢 –

+0

發佈輸出解釋查詢。 – theking963

+1

@AdrianCornish和@ daking963 - 我發佈了'EXPLAIN SELECT SQL_NO_CACHE'結果的屏幕截圖 – Sonny

回答

1

另一種方法可能是將子選擇作爲在日期範圍ONCE之前預先聚合所有查看/點擊的連接,然後加入所有可用的廣告。

SELECT 
     a.id, 
     a.active, 
     a.static, 
     a.position, 
     a.file, 
     a.title, 
     a.url, 
     COALESCE(PreAgg.CntViews, 0) views, 
     COALESCE(PreAgg.CntClicks, 0) clicks 
    FROM 
     ads AS a 
     LEFT JOIN 
     (select lv.ad_id, 
        sum(if(lv.type = 'view', 1, 0)) as CntViews, 
        sum(if(lv.type = 'click', 1, 0)) as CntClicks 
       from 
       ad_log lv 
       where 
        lv.type in ('view', 'click') 
       and lv.created between '2011-01-01 00:00:00' 
            AND '2011-12-31 23:59:59' 
       group by 
        lv.ad_id) PreAgg 
     on A.ID = PreAgg.Ad_ID 

如果基於(類型,創建,ad_id)在Ad_Log表有一個索引可能是更快......這樣一來,對於每一個「類型」將進行分組,然後在每個類型內,跳到日期範圍。所以它應該只需擊中索引的兩個部分...「查看」從/到和「點擊」從/到。而不是每個「廣告ID」,然後檢查類型,然後檢查日期...

+0

我正在爲每個計數列使用子選擇的解決方案,但我喜歡預集合的想法。我的子選擇解決方案比我發佈的查詢快得多,但這似乎更快。 – Sonny

+0

@Sonny,另一種可能性是通過索引來優化內部查詢。請參閱評論 – DRapp

+0

我嘗試添加不同的索引,但它仍然使用外部鍵索引作爲預聚集部分。 – Sonny

2

你可以索引ad_id, type, and created到獲得更快的結果。

This是一個很好的閱讀如何索引連接。閱讀其他案例,它們是有幫助的。

您可以通過索引GROUP BY列來進一步優化它,但記住更多索引時,寫入速度會更慢。

+0

我會給你一個旋轉。 – Sonny

+0

經過進一步測試後,向索引中添加'created'進一步減慢了查詢速度。 – Sonny