2015-12-31 4 views
5

Моя таблица имеет следующие столбцы:MySQL очень медленный запрос

gamelogs_id (auto_increment primary key) 
player_id (int) 
player_name (varchar) 
game_id (int) 
season_id (int) 
points (int) 

Таблица имеет следующие показатели

+-----------------+------------+--------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table   | Non_unique | Key_name   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+-----------------+------------+--------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| player_gamelogs |   0 | PRIMARY   |   1 | player_gamelogs_id | A   |  371330 |  NULL | NULL |  | BTREE  |   |    | 
| player_gamelogs |   1 | player_name  |   1 | player_name  | A   |  3375 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | points   |   1 | points   | A   |   506 |  NULL | NULL | YES | BTREE  |   ## Heading ##|    | 
| player_gamelogs |   1 | game_id   |   1 | game_id   | A   |  37133 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | season    |   1 | season    | A   |   30 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | team_abbreviation |   1 | team_abbreviation | A   |   70 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | player_id   |   1 | game_id   | A   |  41258 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | player_id   |   2 | player_id   | A   |  371330 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | player_id   |   3 | dk_points   | A   |  371330 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | game_player_season |   1 | game_id   | A   |  41258 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | game_player_season |   2 | player_id   | A   |  371330 |  NULL | NULL | YES | BTREE  |   |    | 
| player_gamelogs |   1 | game_player_season |   3 | season_id   | A   |  371330 |  NULL | NULL |  | BTREE  |   |    | 
+-----------------+------------+--------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 

Я пытаюсь вычислить среднее очков за сезон и игрока до начала в игре. Таким образом, для третьей игры сезона avg_points будет средним количеством игр 1 и 2. Номера игр в следующем порядке, так что более ранняя игра меньше, чем более поздняя. У меня также есть возможность использовать поле даты, но я решил, что числовое сравнение будет быстрее?

Мой запрос выглядит следующим образом:

SELECT game_id, 
     player_id, 
     player_name, 
     (SELECT avg(points) 
      FROM player_gamelogs t2 
     WHERE t2.game_id < t1.game_id 
      AND t1.player_id = t2.player_id 
      AND t1.season_id = t2.season_id) AS avg_points 
    FROM player_gamelogs t1 
ORDER BY player_name, game_id; 

EXPLAIN производит следующий вывод:

| id | select_type  | table | type | possible_keys      | key | key_len | ref | rows | Extra           | 
+----+--------------------+-------+------+--------------------------------------+------+---------+------+--------+-------------------------------------------------+ 
| 1 | PRIMARY   | t1 | ALL | NULL         | NULL | NULL | NULL | 371330 | Using filesort         | 
| 2 | DEPENDENT SUBQUERY | t2 | ALL | game_id,player_id,game_player_season | NULL | NULL | NULL | 371330 | Range checked for each record (index map: 0xC8) | 

Я не уверен, если это из-за характера задачи вовлеченного или из-за неэффективности в моем запросе. Спасибо за любые предложения!

+0

Сколько строк есть на player_gamelogs? И попытались ли вы выполнить план объяснения вашего запроса? –

+0

Просто подумайте, почему вы выбираете avg_points, в том же select с подселекцией этого именованного поля. Возможно, вы используете какую-то рекурсивную проблему. Попробуйте удалить этот столбец 'avg_points'. Посмотрите, что произойдет. Не уверен, хотя. –

+0

~ 250 000 строк. Ошибка avg_points при отправке вопроса в Stack Overflow - это не в самом запросе. – user515663

ответ

7

Пожалуйста, обратите внимание этот запрос:

SELECT t1.season_id, t1.game_id, t1.player_id, t1.player_name, AVG(COALESCE(t2.points, 0)) AS average_player_points 
FROM player_gamelogs t1 
     LEFT JOIN player_gamelogs t2 ON 
       t1.game_id > t2.game_id 
      AND t1.player_id = t2.player_id 
      AND t1.season_id = t2.season_id 
GROUP BY 
    t1.season_id, t1.game_id, t1.player_id, t1.player_name 
ORDER BY t1.player_name, t1.game_id; 

Примечания:

  • Для оптимальной работы, вам потребуется дополнительный индекс (season_id, game_id, player_id, PLAYER_NAME)
  • Еще лучше , было бы иметь таблицу игроков, где можно получить имя из идентификатора. Мне кажется излишним, что мы должны захватить имя игрока из таблицы журналов, более того, если это необходимо в индексе.
  • Group by уже сортируется по сгруппированным столбцам. Если можно, не заказывайте впоследствии, поскольку он создает бесполезные накладные расходы. Как указано в комментариях, это не официальное поведение, и результат принятия его согласованности с течением времени должен быть задуман против риска внезапного проигрыша сортировки.
+0

Как получилось? – Sebas

+1

«Группа уже сортируется по сгруппированным столбцам» является рискованным предположением, которое может привести к ошибкам. Единственным условием, гарантирующим сортировку результата, является ORDER BY. –

+0

@Used_By_Это правильно. Я чувствовал, что это все еще было ценной обратной связью, так как это влияет на производительность. – Sebas

2

Ваш запрос в порядке, как написано:

SELECT game_id, player_id, player_name, 
     (SELECT avg(t2.points) 
     FROM player_gamelogs t2 
     WHERE t2.game_id < t1.game_id AND 
       t1.player_id = t2.player_id AND 
       t1.season_id = t2.season_id 
    ) AS avg_points 
FROM player_gamelogs t1 
ORDER BY player_name, game_id; 

Но для оптимальной производительности вы хотите два композитных индексов на нем: (player_id, season_id, game_id, points) и (player_name, game_id, season_id).

Первый индекс должен ускорить подзапрос. Второй - для внешнего order by.

+0

Несмотря на общую теорию «индекса покрытия», мне было интересно, будут ли точки иметь смысл для этого запроса. Я бы попробовал без него сначала, я думаю ... – Sebas

+0

@Sebas. , , «точки» явно не так важны, как 'player_id' и' season_id'. Но индекс покрытия позволяет движку избежать использования страниц данных, что экономит на вводе/выводе. –

1

Поскольку у вас есть запрос сейчас, вы работаете для игры EACH и всех игр под ним для каждого игрока ... Итак, например, если у вас было 10 игр на человека, вы получаете следующие результаты за сезон/лицо

Game 10, Game 10 points, avg of games 1-9 
Game 9, Game 9 points, avg of games 1-8... 
... 
... 
Game 2, Game 2 points, avg of thus final game 1 only. 

Вы заявили, что хотите самую последнюю игру со средним значением всего, что находится под ней. Тем не менее, я предполагаю, что вы не заботитесь о каждом нижнем уровне игры на человека.

Вы также выполняете запрос, охватывающий ВСЕ сезоны. Если сезон закончен, вам нравятся старые времена года? или только в текущем сезоне. В противном случае вы будете проходить через все сезоны, все игроки ...

Все, что сказано, я предлагаю следующее. Во-первых, ограничьте запрос каким-либо последним сезоном, используя предложение WHERE, но я НАСТОЯТЕЛЬНО оставляю сезон в запросе/группе на случай, если вы хотите другие сезоны. Затем я получаю МАКСИМАЛЬНУЮ игру для данного человека/сезона в качестве базовой линии для финальной 1 строки (для каждого сезона), а затем получает среднее значение всего этого. Таким образом, в примере сценария из 10 игр до 2 я не буду захватывать базовые строки 9-2, просто вернув игру №10 по моему сценарию.

select 
     pgMax.Player_ID, 
     pgMax.Season_ID, 
     pgMax.mostRecentGameID, 
     pgl3.points as mostRecentGamePoints, 
     pgl3.player_name, 
     coalesce(avg(pgl2.points), 0) as AvgPointsPriorToCurrentGame 
    from 
     (select pgl1.player_id, 
       pgl1.season_id, 
       max(pgl1.game_id) as mostRecentGameID 
      from 
       player_gameLogs pgl1 
      where 
       pgl1.season_id = JustOneSeason 
      group by 
       pgl1.player_id, 
       pgl1.season_id) pgMax 

     JOIN player_gamelogs pgl pgl2 
      on pgMax.player_id = pgl2.player_id 
      AND pgMax.season_id = pgl2.season_id 
      AND pgMax.mostRecentGameID > pgl2.game_id 

     JOIN player_gamelogs pgl pgl3 
      on pgMax.player_id = pgl3.player_id 
      AND pgMax.season_id = pgl3.season_id 
      AND pgMax.mostRecentGameID = pgl3.game_id 
    group by 
     pgMax.Player_ID, 
     pgMax.Season_ID 
    order by 
     pgMax.Player_ID 

Теперь для оптимизации запроса, составной индекс был бы лучшим (player_id, season_id, game_id, точки). ОДНАКО, если вы ищете только то, что есть в текущем сезоне, укажите свой индекс (season_id, player_id, game_id, точки), поставив SEASON ID в первую позицию для предквалификации предложения WHERE.

Смежные вопросы