优化MySQL查询以避免“使用where;使用临时;使用filesort”

时间:2009-07-07 12:15:39

标签: sql mysql performance optimization

我使用MySQL为我的网站构建了一个自定义论坛。列表页面基本上是一个包含以下列的表格:主题上次更新#回复

数据库表格包含以下列:

id
name
body
date
topic_id
email

主题的topic_id为“0”,回复的主题为topic_id。

SELECT SQL_CALC_FOUND_ROWS
    t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies
FROM
    wp_pod_tbl_forum t
LEFT OUTER JOIN
    wp_pod_tbl_forum r ON (r.topic_id = t.id)
WHERE
    t.topic_id = 0
GROUP BY
    t.id
ORDER BY
    date DESC LIMIT 0,20;

此表中总共有2,100项,查询通常需要6秒钟。我在“topic_id”列中添加了一个INDEX,但这没有多大帮助。有没有办法加速这个查询而不进行重大重组?

编辑:尚未完成工作。我似乎无法让下面的例子正常工作。

2 个答案:

答案 0 :(得分:7)

SELECT  id, name, last_reply, replies
FROM    (
        SELECT  topic_id, MAX(date) AS last_reply, COUNT(*) AS replies
        FROM    wp_pod_tbl_forum
        GROUP BY
                topic_id
        ) r
JOIN    wp_pod_tbl_forum t
ON      t.topic_id = 0
        AND t.id = r.topic_id
UNION ALL
SELECT  id, name, date, 0
FROM    wp_pod_tbl_forum t
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    wp_pod_tbl_forum r
        WHERE   r.topic_id = t.id
        )
        AND t.topic_id = 0
ORDER BY
       date DESC
LIMIT 0, 20

如果您的表格为MyISAMid不是PRIMARY KEY,则需要在(topic_id, id)上创建复合索引。

如果您的表格为InnoDBidPRIMARY KEY,则(topic_id)上的索引将会id隐式添加到索引中)。

<强>更新

如果您在(topic_id, id)(date, id)上有索引,则此查询的效率可能会更高:

请参阅我的博客中有关效果详情的文章:

此查询在30 ms行样本数据的100,000中完成:

SELECT  id, name, last_reply,
        (
        SELECT  COUNT(*)
        FROM    wp_pod_tbl_forum fc
        WHERE   fc.topic_id = fl.topic_id
        ) AS replies
FROM    (
        SELECT  topic_id, date AS last_reply
        FROM    wp_pod_tbl_forum fo
        WHERE   id = (
                SELECT  id
                FROM    wp_pod_tbl_forum fp
                WHERE   fp.topic_id = fo.topic_id
                ORDER BY
                        fp.date DESC, fp.id DESC
                LIMIT 1
                )
                AND fo.topic_id <> 0
        ORDER BY
                fo.date DESC, fo.id DESC
        LIMIT 20
        ) fl
JOIN    wp_pod_tbl_forum ft
ON      ft.id = fl.topic_id
UNION ALL
SELECT  id, name, date, 0
FROM    wp_pod_tbl_forum t
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    wp_pod_tbl_forum r
        WHERE   r.topic_id = t.id
        )
        AND t.topic_id = 0
ORDER BY
       last_reply DESC, id DESC
LIMIT  20

此查询需要两个索引才能有效。

如果您的表格为InnoDB idPRIMARY KEY,则您可以忽略上述indexes中的ID。

答案 1 :(得分:1)

您可能希望将其分解为一组子查询(作为内部查询)。我需要架构真正发挥,但如果你

SELECT t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies  
FROM (
   SELECT (id, name, date)
   FROM wp_pod_tbl_forum
   WHERE topic_id = 0  
) as t 
LEFT OUTER JOIN
   wp_pod_tbl_forum r
WHERE
   r.topic_id = t.id
GROUP BY
    t.id
ORDER BY
    date DESC LIMIT 0,20;

可能有助于加快速度,甚至可能不是最好的答案(错误可能存在)。

有很多方法可以做到这一点,但是当SQL调优是在执行操作之前尽可能地减少每个集合时最重要的事情。