【问题标题】:Optimizing simple query优化简单查询
【发布时间】:2010-11-11 21:31:17
【问题描述】:

此查询将显示给定论坛中的所有未读主题。 EXPLAIN EXTENDED 的输出有点令人担忧。我想知道这里是否有人可以提供一些关于我如何优化的见解。

SELECT topic.*
FROM topic
INNER JOIN board ON topic.board_id = board.id OR topic.board_id = board.mirror_board_id
INNER JOIN category ON board.category_id = category.id
INNER JOIN group_assoc
 ON (
  group_assoc.board_id = board.id AND
  group_assoc.group_id IN (4,15,18,22) AND
  group_assoc.viewable = 1
 )
WHERE topic.last_post_time > 1288278402
AND category.forum_id = 2
AND board.id NOT IN(4,3)
AND NOT EXISTS (
    SELECT *
    FROM topic_read_assoc
    WHERE topic_id = topic.id
    AND member_id = 332
)
ORDER BY topic.last_post_time DESC

输出:

id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY category ref PRIMARY,forum_id_2 forum_id_2 4 const 5 100.00 Using temporary; Using filesort
1 PRIMARY board ref PRIMARY,mirror_board_id,category_id_2 category_id_2 4 source_forum.category.id 4 100.00 Using where
1 PRIMARY group_assoc ref board_id,board_id_2,board_id_3 board_id_3 4 source_forum.board.id 4 100.00 Using where; Using index
1 PRIMARY topic ALL board_id_2 NULL NULL NULL 2462 100.00 Range checked for each record (index map: 0x4)
2 DEPENDENT SUBQUERY topic_read_assoc ref topic_id topic_id 8 source_forum.topic.id,const 1 100.00 Using index

【问题讨论】:

  • 是的。非常简单的查询。 :)
  • @zod:为什么,它垂直和水平都适合屏幕!

标签: mysql optimization


【解决方案1】:

topic (last_post_time) 上创建索引。

您也可以从EXISTS 子查询中删除LIMIT 1,这是多余的。

【讨论】:

    【解决方案2】:

    与其对 group_assoc 表使用 EXISTS,不如为其使用内部连接(这实际上是通过“WHERE board_id = board.id”发生的。将过滤信息放在 WHERE 子句中。

    topic_read_assoc 也是如此 - 在主题 ON topic_id = topic.id 上使用内部联接。

    另外,这只是为了让它更容易,您可以对 Board.id 使用 IN 语法,所以它只有这样的一行:

    WHERE ... board.id 不在 (3,4) 中

    EDIT> 正如 Quassnoi 在下面正确指出的那样,简单地添加内部连接会导致重复。所以使用你想看到的 DISTINCT 或 GROUP BY。顺便说一句,查询不应该使用 SELECT * 。如果将一列添加到该表中,则您的代码可能会被破坏(不是在查询本身中,而是在对结果所做的事情中)。

    【讨论】:

    • EXISTS 本身还不错,但在大多数情况下 JOIN 会表现得更好。
    • @Konerak:具体是哪些情况?
    • 将表放入 JOIN 将使优化器更容易完成其任务。此外,EXISTS 本身并不坏,但是 JOIN 会更容易阅读并且更加一致(因为所有 WHERE 语句都将限制结果而不是连接表)。
    • @Mark: 既然子查询可以返回多个值,那么如何将其重写为join?
    • @Quassnoi,由于子查询(或 EXISTS 语句)已经作为连接起作用,它将限制记录,但不会限制在一条记录(除非它已经这样做了)。跨度>
    【解决方案3】:

    这将是我在论坛中显示所有未读主题的查询

    select * from forum_topic where forum_id = 1 and num_views = 0;
    

    简单示例如下:

    -- TABLES
    
    drop table if exists forum;
    create table forum
    (
    forum_id int unsigned not null auto_increment primary key,
    title varchar(255) unique not null,
    num_topics int unsigned not null default 0
    )engine=innodb;
    
    
    drop table if exists forum_topic;
    create table forum_topic
    (
    topic_id int unsigned not null auto_increment primary key,
    forum_id int unsigned not null,
    subject varchar(255) unique not null,
    num_views int unsigned not null default 0,
    num_replies int unsigned not null default 0,
    key (forum_id)
    )engine=innodb;
    
    delimiter #
    
    create trigger forum_topic_after_ins_trig after insert on forum_topic
    for each row
    begin
     update forum set num_topics=num_topics+1 where forum_id = new.forum_id;
    end#
    
    delimiter ;
    
    -- STORED PROCEDURES
    
    drop procedure if exists get_forum_topic;
    
    delimiter #
    
    create procedure get_forum_topic
    (
    in p_topic_id int unsigned
    )
    begin
        update forum_topic set num_views=num_views+1 where topic_id = p_topic_id;
        select * from forum_topic where topic_id = p_topic_id;
    end #
    
    delimiter ;
    
    -- TEST DATA
    
    insert into forum (title) values ('forum1'),('forum2');
    
    insert into forum_topic (forum_id, subject) values 
    (1,'forum 1 topic 1'), (1,'forum 1 topic 2'),
    (2,'forum 2 topic 1'), (2,'forum 2 topic 2');
    
    -- TESTING 
    
    call get_forum_topic(1);
    call get_forum_topic(3);
    call get_forum_topic(2);
    call get_forum_topic(3);
    call get_forum_topic(2);
    call get_forum_topic(3);
    
    select * from forum;
    select * from forum_topic;
    
    select * from forum_topic where num_views = 0;
    

    【讨论】:

    • 我更愿意坚持使用基本查询,而不是冒险进入视图、触发器或过程
    • 我将再次为您重复 BASIC 查询“从 forum_topic 中选择 *,其中 forum_id = 1 和 num_views = 0”您如何设法实现这一点,即使用计数器字段加上 sprocs 和 tiggers 与一些需要大量 db 往返的内联 sql (eeek) 由您决定,但让我们在这里透视一下,前面提到的是 BASIC 查询,您的查询有点错误...冗长。
    猜你喜欢
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    相关资源
    最近更新 更多