Наименьшее количество кода, возможное для этого запроса MySQL?


У меня есть запрос MySQL, который:

  • получает данные из трех таблиц, связанных уникальными идентификаторами.
  • подсчитывает количество игр, сыгранных в каждой категории, от каждого пользователя
  • и подсчитывает количество игр, в которые играл каждый пользователь, попадающих в категорию "fps".

Мне кажется, что этот код может быть намного меньше. Как бы я мог сделать этот запрос меньше. http://sqlfiddle.com/#!2 / 6d211/1

Любая помощь ценится, даже если вы просто дайте мне ссылки, чтобы проверить.

2 2

2 ответа:

Как правило, неплохо иметь логику соединения как часть предложения [Inner|Left] Join, а не как часть предложения Where. В случае упрощения запроса это очищает предложение Where, чтобы обработчик запросов не применял условия фильтра слишком рано, что ограничивает то, что вы хотите сделать в более сложных частях запроса (и влияет на общую производительность запроса).

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

Вот что я придумал:

SELECT
    u.user_id
    ,pg.game_id
    ,u.user
    ,g.game
    ,g.game_cat
    ,ga.cat_count
    ,ga.fps_count
FROM users u
inner join played_games pg
    on u.user_id = pg.user_id
inner join games g
    on pg.game_id = g.id
inner join
(
    select 
        ipg.user_id
        ,ig.game_cat
        ,count(ig.game) cat_count
        ,sum(case when ig.game_cat = 'fps' then 1 else 0 end) fps_count
    from played_games ipg
    inner join games ig
        on ipg.game_id = ig.id
    group by
        ipg.user_id
        ,ig.game_cat
) ga
    on g.game_cat = ga.game_cat
    and pg.user_id = ga.user_id
order by
    ga.fps_count desc
    ,u.user
    ,ga.cat_count desc;

Одно из отличий исходного запроса (кроме небольшого переименования) состоит в том, что поле fps_count имеет значение 0 вместо NULL для игроков, которые не играли ни в одну игру FPS. Надеюсь, что это не так критично, а скорее помогает добавить смысл в запрос.

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

Надеюсь, это поможет.

Я думал, предоставить ли решение d_mcg или это. Я решил пойти на это. Мне было интересно, какой из них будет быстрее. Это то, что вы можете попробовать и рассказать нам :)

select u.user_id, pg.game_id, u.user, g.game, g.game_cat,
  (select count(*) from played_games pg2
   join games g2 on pg2.game_id = g2.id
   where pg2.user_id = pg.user_id and g2.game_cat = g.game_cat) cat_count,
  (select count(*) from played_games pg3
   join games g3 on pg3.game_id = g3.id
   where pg3.user_id = pg.user_id and g3.game_cat = g.game_cat and
     g.game_cat = 'fps') order_count
from users u
left join played_games pg on u.user_id = pg.user_id
join games g on pg.game_id = g.id
order by order_count desc, u.user, cat_count desc