MySQL查询太慢了?

时间:2014-12-07 21:42:07

标签: 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秒内执行。

我可以做些什么来提高性能吗?

1 个答案:

答案 0 :(得分:1)

首先 - 永远不要那样做:

AND IF(EWRporta2_articles.article_date IS NULL, xf_thread.post_date
           , EWRporta2_articles.article_date) < 1417987751

使用功能可以防止任何数据库使用索引来优化像这样的表达式
将其重写为以下形式:

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 AND(Y OR Z)=&gt; 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,那么日期总是为NULL,我们可以跳过冗余排序操作,因为它是浪费时间。