2014-12-07 165 views
0

所以我有一個查詢:MySQL查詢太慢?

SELECT EWRporta2_articles.*, xf_thread.*, xf_forum.*, xf_user.*, xf_post.message, 
    IF(NOT ISNULL(xf_user.user_id), xf_user.username, xf_thread.username) AS username 
FROM EWRporta2_articles 
    INNER JOIN xf_thread ON (xf_thread.thread_id = EWRporta2_articles.thread_id) 
    INNER JOIN xf_forum ON (xf_forum.node_id = xf_thread.node_id) 
    INNER JOIN xf_post ON (xf_post.post_id = xf_thread.first_post_id) 
    LEFT JOIN xf_user ON (xf_user.user_id = xf_thread.user_id) 
WHERE EWRporta2_articles.article_date < 1417987751 
    AND xf_thread.discussion_state = 'visible' 
ORDER BY EWRporta2_articles.article_date DESC 
LIMIT 0, 5 

該查詢在0.0012秒執行......那好。這個查詢所做的是查詢文章列表,然後將它們鏈接到我論壇上的一個主題。

但是,我試圖稍微改變查詢。雖然上面的查詢要求線程存在文章行。我想查找鏈接到特定文章或存在於特定論壇節點ID中的線索。因此,即使線程中不存在文章行,但如果它具有特定的node_id,它仍會顯示出來。這是我對此的查詢:

SELECT EWRporta2_articles.*, xf_thread.*, xf_forum.*, xf_user.*, xf_post.message, 
    IF(EWRporta2_articles.article_date IS NULL, xf_thread.post_date, EWRporta2_articles.article_date) AS article_date, 
    IF(NOT ISNULL(xf_user.user_id), xf_user.username, xf_thread.username) AS username 
FROM xf_thread 
    LEFT JOIN EWRporta2_articles ON (EWRporta2_articles.thread_id = xf_thread.thread_id) 
    INNER JOIN xf_forum ON (xf_forum.node_id = xf_thread.node_id) 
    INNER JOIN xf_post ON (xf_post.post_id = xf_thread.first_post_id) 
    LEFT JOIN xf_user ON (xf_user.user_id = xf_thread.user_id) 
WHERE (xf_thread.node_id IN ('66','78') OR EWRporta2_articles.article_date IS NOT NULL) 
    AND IF(EWRporta2_articles.article_date IS NULL, xf_thread.post_date, EWRporta2_articles.article_date) < 1417987751 
    AND xf_thread.discussion_state = 'visible' 
ORDER BY article_date DESC 
LIMIT 0, 5 

此查詢的問題是它在0.5683秒內執行。

我能做些什麼來提高性能嗎?

+2

你沒有提供關於你的表的信息,任何存在的索引,也沒有提供'EXPLAIN'的結果。沒有細節回答這個問題是非常困難的。 – 2014-12-07 21:45:52

+0

除了'explain',''或'可能很難優化。把它寫成由'union'連接的兩個子查詢可能會更好。 – 2014-12-07 21:57:38

+0

只是好奇,是否有一個情況下,user.user_id是NULL,但user.username不是NULL?!?! – Strawberry 2014-12-08 00:17:27

回答

1

首先 - 永遠不要做:

​​

使用functon防止任何數據庫,以便優化這樣一個表達式使用索引。

把它改寫成這種形式:

EWRporta2_articles.article_date IS NULL AND xf_thread.post_date < 1417987751 
OR 
EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 

現在使用布爾代數的法律,簡化了查詢的條件, 特別是在利用法律:Distirbutivity of AND over ORhttp://en.wikipedia.org/wiki/Boolean_algebra
x和(y OR Z)=> X和Y或X和Z



如果您將此法應用於此條件:

WHERE 
(xf_thread.node_id IN ('66','78') OR EWRporta2_articles.article_date IS NOT NULL) 
AND 
(
    EWRporta2_articles.article_date IS NULL AND xf_thread.post_date < 1417987751 
    OR 
    EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 
) 

您將獲得:

xf_thread.node_id IN ('66','78') AND EWRporta2_articles.article_date IS NULL AND xf_thread.post_date < 1417987751 
OR 
xf_thread.node_id IN ('66','78') AND EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 
OR 
EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date IS NULL AND xf_thread.post_date < 1417987751 
OR 
EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 

第三個條件是總是假的,所以我們可以跳過它:

EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date IS NULL ... 

第二個和第四一個:

xf_thread.node_id IN ('66','78') AND EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 
OR 
EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 

可以簡化爲:

EWRporta2_articles.article_date IS NOT NULL AND EWRporta2_articles.article_date < 1417987751 

,它可以進一步簡化爲:

EWRporta2_articles.article_date < 1417987751 

最後,我們將得到:

WHERE (
    xf_thread.node_id IN ('66','78') 
    AND 
    EWRporta2_articles.article_date IS NULL 
    AND  
    xf_thread.post_date < 1417987751 
    OR 
    EWRporta2_articles.article_date < 1417987751 
) 
    AND xf_thread.discussion_state = 'visible' 

現在劃分查詢分成兩個單獨的子查詢,那麼工會的這樣的結果:

SELECT * 
FROM (
    SELECT ...... 
    JOIN ... JOIN ... JOIN ... 
    WHERE 
     xf_thread.node_id IN ('66','78') 
     AND EWRporta2_articles.article_date IS NULL 
     AND xf_thread.post_date < 1417987751 
     AND xf_thread.discussion_state = 'visible' 
    -- ORDER BY article_date DESC 
    LIMIT 0, 5 
) q1 
UNION 
SELECT * 
FROM (
    SELECT ...... 
    JOIN ... JOIN ... JOIN ... 
    WHERE 
     EWRporta2_articles.article_date < 1417987751 
     AND xf_thread.discussion_state = 'visible' 
    ORDER BY article_date DESC 
    LIMIT 0, 5 
) q2 
ORDER BY article_date DESC 
LIMIT 0, 5 

注意,在第一子查詢中的條款ORDER BY article_date DESC被跳過 - 由於條件說:AND EWRporta2_articles.article_date IS NULL,那麼日期總是空的,我們可以跳過冗餘的排序操作,因爲這是浪費時間。

+0

一些別名本來不錯 – Strawberry 2014-12-08 00:18:16

+0

我得到一個錯誤使用此代碼#1060 - 重複的列名'thread_id'' – 2014-12-08 01:29:22