简体   繁体   English

mySQL - 如何解释我的EXPLAIN结果并优化此查询?

[英]mySQL - How can I interpret my EXPLAIN results and optimize this query?

Looking to understand what my EXPLAIN results mean here, and to optimize this query and my tables as best as I can. 想要了解我的EXPLAIN结果在这里意味着什么,并尽可能地优化这个查询和我的表。

The query: 查询:

SELECT i.pending,
       i.itemid, 
       i.message,
       i.cid, 
       i.dateadded, 
       i.entrypoint,  
       SUM(CASE WHEN v.direction = 1 THEN 1
                     WHEN v.direction = 2 THEN -1
                     ELSE 0 END) AS votes,
       c.name AS cname,
       c.tag AS ctag,
       i.userid,
       (SELECT COUNT(commentid) FROM `comments` WHERE comments.itemid = i.itemid) AS commentcount,
       CASE WHEN NOT EXISTS (SELECT voteid FROM `votes` WHERE votes.itemid = i.itemid AND votes.userid = @userid) THEN '0' ELSE '1' END AS hasVoted,
       CASE WHEN NOT EXISTS (SELECT voteid FROM `user_favorites` WHERE user_favorites.itemid = i.itemid AND user_favorites.userid = @userid) THEN '0' ELSE '1' END AS isFavorite
    FROM `contentitems` i
      LEFT JOIN votes v ON i.itemid = v.itemid
      LEFT JOIN `user_favorites` uv ON i.itemid = uv.itemid AND (uv.userid = @userid)
      INNER JOIN  `categories` c ON i.cid = c.cid
    GROUP BY i.itemid
    HAVING SUM(CASE WHEN v.direction = 1 THEN 1
                    WHEN v.direction = 2 THEN -1
                    ELSE 0 END) > -3 AND i.pending = 0
    ORDER BY i.dateadded DESC

(Edited Formatting) (编辑格式)

The explain results: 解释结果如下:

+----+--------------------+----------------+--------+-------------------------+-------------------------+---------+------------------------+------+-------------------------------------------------------
| id |    select_type     |     table      |  type  |      possible_keys                  key                               | key_len | ref                     | rows |              Extra              |
+----+--------------------+----------------+--------+-------------------------+-------------------------+---------+------------------------+------+------------------------------------------------------+
|  1 | PRIMARY            | i              | ALL    | NULL                              | NULL                              | NULL    | NULL                    |  121 | Using temporary; Using filesort |
|  1 | PRIMARY            | v              | ref    | fk_contentitemsitemid_votesitemid | fk_contentitemsitemid_votesitemid | 4       | db33481_mydb.i.itemid   |    2 |                                 |
|  1 | PRIMARY            | uv             | ALL    | NULL                              | NULL                              | NULL    | NULL                    |    7 |                                 |
|  1 | PRIMARY            | c              | eq_ref | PRIMARY                           | PRIMARY                           | 4       | db33481_mydb.i.cid      |    1 |                                 |
|  4 | DEPENDENT SUBQUERY | user_favorites | ALL    | NULL                              | NULL                              | NULL    | NULL                    |    7 | Using where                     |
|  3 | DEPENDENT SUBQUERY | votes          | ref    | fk_contentitemsitemid_votesitemid | fk_contentitemsitemid_votesitemid | 4       | func                    |    2 | Using where                     |
|  2 | DEPENDENT SUBQUERY | comments       | ALL    | NULL                              | NULL                              | NULL    | NULL                    |   26 | Using where                     |
+----+--------------------+----------------+--------+-------------------------+-------------------------+---------+------------------------+------+------------------------------------------------------+

First, you have a select not exists vote ID, then do a left-join in the from, and finally a sum in the having. 首先,你有一个选择不存在的投票ID,然后在from中进行左连接,最后在有一个和。 This is hitting your votes table 3 times. 这是你的投票表3次。 IF each vote is possibly associated to a single "ItemID", then that would be best to be pre-aggregated by itself as its own "Sum" done ONCE. 如果每个投票可能与单个“ItemID”相关联,那么最好将其自身预先汇总为其自己的“Sum”完成ONCE。

Additionally, since your final "HAVING" clause is a direct basis of the Votes, having a left join on votes becomes a dead point and ultimately ends in a normal JOIN. 此外,由于你的最终“HAVING”条款是投票的直接基础,因此在投票上进行左连接将成为一个死点并最终以正常的JOIN结束。

All that being said, I would pre-query FIRST on the votes that FINISH with the qualifying HAVING condition up front, then join to the content items and other joins... The query against User_Favorites is a count and will either be 0 (not found), or 1 (found). 所有这一切,我会预先查询FIRST与预先确定合格的HAVING条件的FINISH,然后加入内容项和其他联接...对User_Favorites的查询是一个计数,将为0(不是发现),或1(找到)。 There should be no need for a case/when 不需要案件/什么时候

My first query alias "PQ" represents the "PreQuery" 我的第一个查询别名“PQ”代表“PreQuery”

SELECT
      PQ.ItemID,
      PQ.VSum as Votes,
      PQ.HasVoted,
      i.pending,
      i.itemid, 
      i.message,
      i.cid, 
      i.dateadded, 
      i.entrypoint,  
      i.userid,
      c.name AS cname,
      c.tag AS ctag,
      ( SELECT COUNT(commentid) 
           FROM `comments` 
           WHERE comments.itemid = PQ.itemid) AS commentcount,
      ( SELECT COUNT(*) FROM user_favorites uf
              WHERE uf.itemid = PQ.itemid 
                AND uf.userid = @userid ) AS isFavorite
   from 
      ( SELECT
              v.itemid,
              SUM( case when v.Direction = 1 then 1
                        when v.Direction = 2 then -1
                        ELSE 0 end ) as VSum,
              MAX( if( votes.userid = @userid, 1, 0 ) AS HasVoted 
           from 
              votes v
           group by 
              v.itemid
           having
              VSum > -3 ) PQ

         JOIN ContentItems i
            ON PQ.ItemID = i.ItemID
            and i.Pending = 0

         JOIN Categories c
            ON i.cid = c.cid

   ORDER BY 
      i.dateadded DESC

Others have indicated the need for indexes, agreed. 其他人已表示需要索引,同意。 I would ensure each table has respective index on either the user ID or Item ID (or both where appropriate). 我会确保每个表都有关于用户ID或项ID的相应索引(或两者都适当)。

Couple other points... You originally start query querying all ContentItems, but left-joining to votes... But then applying the element of a user ID. 结合其他点...你最初开始查询查询所有ContentItems,但是左边加入投票...但是然后应用用户ID的元素。 This DEFINITELY smells of a query for a specific user. 这绝对是特定用户查询的气味。 That being said, I would ADDITIONALLY pre-start the entire query with a select of only ItemIDs the user ID has done anything with... THEN continue the query. 话虽这么说,我会额外预先启动整个查询,只选择一个ItemID,用户ID已经做了什么...然后继续查询。

I see that there is no key used to access comments , votes , and user_favorites . 我看到没有用于访问commentsvotesuser_favorites Unless the tables are really small you should try adding an index on userid and itemid in those tables. 除非表格非常小​​,否则您应该尝试在这些表格中的useriditemid上添加索引。

Try seeing this link for understanding explain plan. 尝试查看此链接以了解解释计划。 Try going down that section, it clearly explains what you need to look for. 尝试下去那一节,它清楚地解释了你需要寻找什么。

More over your explain plan looks like having less info. 更多关于你的解释计划看起来更少的信息。 please try using sql developer from oracle. 请尝试从oracle使用sql developer it is open source and does give you apt details about the explain plan. 它是开源的,并且确实为您提供有关解释计划的详细信息。

I would add the following indexes: 我会添加以下索引:

ALTER TABLE comments ADD INDEX (commentid)
ALTER TABLE user_favorites ADD INDEX (itemid, voteid)

In addition, if the possible_keys column says NULL, it means that there are no usable keys for that table. 此外,如果possible_keys列显示NULL,则表示该表没有可用的键。 Even if they aren't used for the optimization, they will show up there if they exist for a column in the query. 即使它们不用于优化,如果它们存在于查询中的列中,它们也会显示在那里。 Most likely, you have a primary key on those tables on a column that is not being accessed in the query. 最有可能的是,您在查询中未访问的列上的那些表上有一个主键。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM