Наименьшее количество кода возможно для этого запроса MySQL? - PullRequest
2 голосов
/ 24 марта 2012

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

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

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

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

Ответы [ 2 ]

1 голос
/ 24 марта 2012

Как правило, желательно, чтобы логика соединения была частью предложения [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. Надеюсь, это не так критично, но скорее поможет добавить смысл в запрос.

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

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

0 голосов
/ 24 марта 2012

Я думал, предоставить ли решение 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
...