MySQL - Как я могу интерпретировать мои результаты EXPLAIN и оптимизировать этот запрос? - PullRequest
4 голосов
/ 12 октября 2011

Нужно понять, что означают мои EXPLAIN результаты, и как можно лучше оптимизировать этот запрос и мои таблицы.

Запрос:

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

(отредактированное форматирование)

Результаты объяснения:

+----+--------------------+----------------+--------+-------------------------+-------------------------+---------+------------------------+------+-------------------------------------------------------
| 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                     |
+----+--------------------+----------------+--------+-------------------------+-------------------------+---------+------------------------+------+------------------------------------------------------+

Ответы [ 4 ]

1 голос
/ 24 октября 2011

Во-первых, у вас есть идентификатор выбора, который не существует, затем выполните левое соединение в поле from и, наконец, в сумме.Это попадает в вашу таблицу голосов 3 раза.ЕСЛИ каждый голос, возможно, связан с одним «ItemID», то лучше было бы предварительно агрегировать его как собственную «Sum», выполненную ОДИН РАЗ.

Кроме того, поскольку ваше последнее предложение «HAVING»прямое основание голосов, левая комбинация голосов становится мертвой точкой и, в конечном итоге, заканчивается обычным соединением.

С учетом вышесказанного, я бы предварительно запросил ПЕРВЫЕ голоса, которые ЗАВЕРШАЮТ с подходящим условием HAVING, затем присоединятся к элементам контента и другим объединениям ... Запрос к User_Foving - это подсчет ибудет либо 0 (не найден), либо 1 (найден).Не должно быть необходимости в случае / *

Мой первый псевдоним запроса "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

Другие указали на необходимость индексов, согласились.Я хотел бы убедиться, что у каждой таблицы есть соответствующий индекс либо по идентификатору пользователя, либо по идентификатору предмета (или обоим, где это необходимо).

Пара других точек ... Вы изначально начинаете запрашивать все ContentItems, но оставляете присоединение к голосам... Но затем применяя элемент идентификатора пользователя.Это определенно пахнет запросом для конкретного пользователя.При этом я ДОБАВЬ ПРЕДВАРИТЕЛЬНО предварительно запустил весь запрос с выбором только ItemID, с которыми ID пользователя что-либо делал ... Затем продолжил запрос.

0 голосов
/ 22 октября 2011

Попробуйте просмотреть эту ссылку для понимания плана объяснения.Попробуйте перейти в этот раздел, он четко объясняет, что вам нужно искать.

Более того, ваш план объяснения выглядит так, как будто меньше информации.пожалуйста, попробуйте использовать sql developer от Oracle.это открытый исходный код, и он дает вам точную информацию о плане объяснения.

0 голосов
/ 22 октября 2011

Я бы добавил следующие индексы:

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

Кроме того, если в столбце возможных_ключей указано NULL, это означает, что для этой таблицы нет используемых ключей. Даже если они не используются для оптимизации, они будут отображаться там, если они существуют для столбца в запросе. Скорее всего, у вас есть первичный ключ для этих таблиц в столбце, к которому нет доступа в запросе.

0 голосов
/ 13 октября 2011

Я вижу, что нет ключа, используемого для доступа к comments, votes и user_favorites.Если таблицы не очень маленькие, попробуйте добавить индекс для userid и itemid в этих таблицах.

...