2013-01-21 77 views
0

今天,我收到的電子郵件從我的託管帳戶說,我需要調整我的查詢:與子查詢的MySQL優化查詢

SELECT 
    `id`, `nick`, `msg`, `uid`, `show_pic`, 
    `time`,`ip`,`time_updated`, 
    (SELECT COUNT(c.msg_id) 
    FROM `the_ans` c 
    where c.msg_id = d.id) AS counter, 
    (SELECT c.msg 
    FROM `the_ans` c 
    WHERE c.msg_id=d.id 
    ORDER BY `time` DESC LIMIT 1) as lastmsg 
FROM 
    `the_data` d 
ORDER BY `time_updated` DESC LIMIT 26340 ,15 

說明:

id select_type table type possible_keys key key_len ref rows Extra 
1 PRIMARY d ALL 34309 Using filesort 
3 DEPENDENT SUBQUERY c ALL 43659 Using where; Using filesort 
2 DEPENDENT SUBQUERY c ALL 43659 Using where 

這個查詢檢查65,396,669,012,829行,這在共享主機中是不可接受的。

TBH,我不明白他們的解釋.. 什麼查詢實際上並是時間去15個職位順序更新, 每篇文章我搶的最新評論, 計數每個帖子所有評論。

posts table - 'the_data' 

comments table = 'the_ans' 

我不是一個MySQL的大師,我不知道如何改進這個查詢 任何幫助將不勝感激

THX

查詢

SELECT 
    `id` , `nick` , `msg` , `uid` , `show_pic` , `time` , `ip` , `time_updated` , (
    SELECT COUNT(c.msg_id) 
    FROM `the_ans` c 
    WHERE c.msg_id = d.id 
    ) AS counter, (
    SELECT c.msg 
    FROM `the_ans` c 
    WHERE c.msg_id = d.id 
    ORDER BY `time` DESC 
    LIMIT 1 
    ) AS lastmsg 
FROM `the_data` d 
ORDER BY `time_updated` DESC 
LIMIT 26340 , 15 

這是結果結構

id| nick | msg | uid | show_pick | time  | ip |time_updated|counter|lastmsg 
    |  |  |  |   |   | |   |  | 
7 | jqman | hello| 10074 | 0  |2013-21-01 | 12 |2013-21-01 | 55 |blah bl 
+0

您確定'LIMIT 26340,15'部分? – inhan

+1

基本的問題是需要執行查詢來獲得15行。要執行你的主查詢,每個子查詢都需要爲每一行處理執行。你可以發佈每個表的show create table的結果嗎?你是否在每個表中創建了「id」的索引? – Jaydee

+0

通常,不相關的子查詢將比關聯的子查詢執行得更好。通用語法如下:SELECT x。* FROM my_table x JOIN(SELECT id,MAX(time)max_time FROM my_table GROUP BY id)y ON y.id = x.id AND y.max_time = x.time;這將在每個線程中爲您提供最新的消息。然後,你只需加入其他信息(或反之亦然) – Strawberry

回答

2

在解釋計劃快速瀏覽顯示,沒有適合MySQL使用的索引,所以它採用全表掃描。

EXPLAIN: 
id select_type  table type possible_keys key key_len ref rows Extra 
-- ------------------ ----- ---- ------------- --- ------- --- ----- ---------------------------- 
1 PRIMARY   d  ALL        34309 Using filesort 
3 DEPENDENT SUBQUERY c  ALL        43659 Using where; Using filesort 
2 DEPENDENT SUBQUERY c  ALL        43659 Using where 

爲了優化現有查詢的執行,你需要添加合適的索引。可能的候選人:

ON `the_data`(`time_updated`) 
ON `the_ans`(`msg_id`,`time`) 

這些指標將顯著同時提高外部查詢(可能消除排序操作),以及相關子查詢的衆多處決的性能。


除此之外,您將需要更改查詢以提高性能。整個結果集準備完畢後,將應用最外層查詢中的LIMIT子句,這意味着這兩個相關子查詢將針對表the_data中的每一行執行。這會吃你的午餐,表現明智。

要讓那些相關的子查詢僅針對正在返回的(最多)15行而運行,則需要在這些子查詢運行之前應用LIMIT子句。

此查詢應返回的結果集等同,並且將避免各相關子查詢,這將改善性能相當的34,000+執行:

SELECT d.* 
    , (SELECT COUNT(c.msg_id) 
      FROM `the_ans` c 
      WHERE c.msg_id = d.id 
     ) AS counter 
    , (SELECT c.msg 
      FROM `the_ans` c 
      WHERE c.msg_id = d.id 
      ORDER BY `time` DESC 
      LIMIT 1 
     ) AS lastmsg 
    FROM (SELECT e.`id` 
       , e.`nick` 
       , e.`msg` 
       , e.`uid` 
       , e.`show_pic` 
       , e.`time` 
       , e.`ip` 
       , e.`time_updated` 
      FROM `the_data` e 
      ORDER 
      BY e.`time_updated` DESC 
      LIMIT 26340 , 15 
     ) d 
ORDER BY d.`time_updated` DESC 

(您當前的查詢執行的每一個相關子查詢的「SELECT COUNT(1) FROM the_data」時代。通過上面的重寫查詢,每個子查詢將只執行15次。)

+0

那好吧,似乎最大的問題是我沒有使用索引。 也使用你的方法時,查詢執行速度大約快16%。 thx! – eben

+0

當我們每天增加約1000%的指數時,我們會學到新的東西 – eben

+0

@JQman:是的,沒有索引是最大的問題。這是一個修復程序,可以在不部署任何代碼更改的情況下應用。對於添加了這些索引的查詢,獲取EXPLAIN輸出將會很好。 – spencer7593

1

執行相關子查詢從主查詢選擇時間限制的行:

SELECT d.*, 
     (SELECT COUNT(c.msg_id) 
     FROM `the_ans` c 
     where c.msg_id = d.id) AS counter, 
     (SELECT c.msg 
     FROM `the_ans` c 
     WHERE c.msg_id=d.id 
     ORDER BY `time` DESC LIMIT 1) as lastmsg 
FROM (SELECT 
     `id`, `nick`, `msg`, `uid`, `show_pic`, 
     `time`,`ip`,`time_updated` 
     FROM 
     `the_data` 
     ORDER BY `time_updated` DESC LIMIT 26340 ,15) d 

此外,還要確保你有time_updatedmsg_id指標。

0

像這樣的東西應該會讓你的結果更快一點。

注意,這是使用內部聯接,因爲它的目的是工作,當上* the_data每個記錄*對* the_ans至少一個匹配記錄*

SELECT `id` , `nick` , `msg` , `uid` , `show_pic` , `time` , `ip` , `time_updated` , Sub1.counter, c.msg AS lastmsg 
FROM `the_data` d 
INNER JOIN (SELECT msg_id, COUNT(*) AS counter, MAX(`time`) AS MaxTime FROM `the_ans` GROUP BY msg_id) Sub1 ON d.id = Sub1.msg_id 
INNER JOIN the_ans c ON d.id = c.msg_id AND sub1.MaxTime = c.`time` 
ORDER BY `time_updated` DESC 
LIMIT 26340 , 15