sql查询比预期慢

时间:2012-12-07 16:55:59

标签: sql left-join phpbb

在我显示查询之前,这里是相关的表定义:

CREATE TABLE phpbb_posts (
    topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
    poster_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
    KEY topic_id (topic_id),
    KEY poster_id (poster_id),
);


CREATE TABLE phpbb_topics (
    topic_id mediumint(8) UNSIGNED NOT NULL auto_increment
);

这是我正在尝试的查询:

SELECT p.topic_id, p.poster_id 
FROM phpbb_topics AS t 
LEFT JOIN phpbb_posts AS p 
   ON p.topic_id = t.topic_id 
      AND p.poster_id <> ... 
WHERE p.poster_id IS NULL;

基本上,查询是尝试查找所有主题,其中除目标用户之外的其他人发布的次数为零。换句话说,唯一发布的人是目标用户。

问题是查询花了很长时间。这是解释它:

Array
(
    [id] => 1
    [select_type] => SIMPLE
    [table] => t
    [type] => index
    [possible_keys] =>
    [key] => topic_approved
    [key_len] => 1
    [ref] =>
    [rows] => 146484
    [Extra] => Using index
)
Array
(
    [id] => 1
    [select_type] => SIMPLE
    [table] => p
    [type] => ref
    [possible_keys] => topic_id,poster_id,tid_post_time
    [key] => tid_post_time
    [key_len] => 3
    [ref] => db_name.t.topic_id
    [rows] => 1
    [Extra] => Using where; Not exists
)

我在SQL方面的一般假设是,任何JOIN都是超快的,并且可以立即完成,假设所有相关列都是主键或外键(在这种情况下它们都是)。

我尝试了其他一些问题:

SELECT COUNT(1) 
    FROM phpbb_topics AS t 
    JOIN phpbb_posts AS p 
        ON p.topic_id = t.topic_id;

很快就会返回353340。

然后我做了这些:

SELECT COUNT(1) 
    FROM phpbb_topics AS t 
    JOIN phpbb_posts AS p 
        ON p.topic_id = t.topic_id
            AND p.poster_id <> 77198;

SELECT COUNT(1) 
    FROM phpbb_topics AS t 
    JOIN phpbb_posts AS p 
        ON p.topic_id = t.topic_id
    WHERE p.poster_id <> 77198;

这两个都需要一段时间(15-30秒之间)。如果我更改&lt;&gt;到a =它根本没有时间。

我做了一些不正确的假设吗?也许我的DB只是foobar'd?

3 个答案:

答案 0 :(得分:1)

我认为将phpbb_posts(topic_id)上的索引替换为2个字段的复合索引可以提高查询的效果:

CREATE TABLE phpbb_posts (
topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
poster_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
--KEY topic_id (topic_id), 
KEY topic_id_poster_id (topic_id,poster_id)
KEY poster_id (poster_id),
);

答案 1 :(得分:1)

您的索引看起来对我来说足够了...您是否可以尝试此查询并告诉我性能与原始版本的对比情况?

SELECT sub.topic_id
FROM (
    SELECT t.topic_id
    FROM phpbb_topics AS t 
    WHERE
        EXISTS (
            SELECT *
            FROM phpbb_posts p
            WHERE 
                p.topic_id = t.topic_id
                AND p.poster_id = 77198
        )
) sub
WHERE 
    NOT EXISTS (
        SELECT *
        FROM phpbb_posts p
        WHERE 
            p.topic_id = sub.topic_id
            AND p.poster_id <> 77198
)

我的想法是,通过将主题限制为仅有问题的海报实际发布的主题,反加入(在这种情况下使用NOT EXISTS而不是LEFT JOIN实现)将必须检查除被搜索者之外的海报的主题少得多。

答案 2 :(得分:0)

SELECT t.topic_id 
FROM phpbb_topics AS t 
JOIN phpbb_posts AS p1
   ON p1.topic_id = t.topic_id
      AND p1.poster_id = $poster_id
LEFT JOIN phpbb_posts AS p2 
   ON p2.topic_id = t.topic_id 
      AND p2.poster_id <> $poster_id
WHERE p2.poster_id IS NULL

这让它变得更快了。我收到目标用户发布的所有帖子,其中附有主题信息,然后获取除了目标用户以外的所有人员。

在p1.poster_id列中会有很多重复项,但由于我实际上没有得到那一行,我认为该列中的重复项并不重要。

谢谢!