Проблемы с большим запросом и подзапросом - PullRequest
1 голос
/ 25 января 2009

Я думал, что буду умным и буду использовать подзапрос, чтобы получить мой отчет за один раз. Но после столкновения с проблемами и чтения документации я увидел, что мой подход не работает в MySQL. Мой внутренний запрос возвращает ~ 100 записей, а внешний запрос сканирует 20000 записей. Когда я ограничил внешний запрос до 20 записей, он выполнялся 20 секунд - очень медленно.

Интересно, можно ли как-то его реструктурировать, чтобы внутренний запрос не выполнялся КАЖДЫЙ раз для каждой записи во внешнем запросе?

select p1.surname ,p1.name,p1.id,r1.start_date,r1.end_date,c1.short_name
FROM ejl_players p1
left JOIN ejl_registration r1 ON ( r1.player_id = p1.id )
left JOIN ejl_teams t1 ON ( r1.team_id = t1.id )
left JOIN ejl_clubs c1 ON ( t1.club_id = c1.id )
where  r1.season=2008
and p1.id in
 (
SELECT p.id
FROM ejl_players p 
left JOIN ejl_registration r ON (r.player_id = p.id) 
left JOIN ejl_teams t ON (r.team_id = t.id) 
left JOIN ejl_clubs c ON (t.club_id = c.id)
WHERE r.season = 2008
GROUP BY p.id
HAVING COUNT(DISTINCT c.id)  > 1
)

Объясните (я ограничил внешний запрос максимум 20 записями:

id  select_type  table  type  possible_keys  key  key_len  ref  rows  Extra  
1 PRIMARY p1 range PRIMARY PRIMARY 4 NULL 19 Using where 
1 PRIMARY r1 ref team_id,season season 10 const,d17528sd14898.p1.id 1 Using where 
1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 d17528sd14898.r1.team_id 1   
1 PRIMARY c1 eq_ref PRIMARY PRIMARY 4 d17528sd14898.t1.club_id 1   
2 DEPENDENT SUBQUERY p index PRIMARY PRIMARY 5 NULL 23395 Using index 
2 DEPENDENT SUBQUERY r ref team_id,season season 10 const,d17528sd14898.p.id 1 Using where; Using index 
2 DEPENDENT SUBQUERY t eq_ref PRIMARY PRIMARY 4 d17528sd14898.r.team_id 1   
2 DEPENDENT SUBQUERY c eq_ref PRIMARY PRIMARY 4 d17528sd14898.t.club_id 1 Using index 

Ответы [ 2 ]

5 голосов
/ 25 января 2009

Попробуйте использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ (как-то так):

SELECT p1.surname ,p1.name,p1.id,r1.start_date,r1.end_date,c1.short_name
FROM ejl_players p1
INNER JOIN (
    SELECT p.id
    FROM ejl_players p 
    LEFT JOIN ejl_registration r ON (r.player_id = p.id) 
    LEFT JOIN ejl_teams t ON (r.team_id = t.id) 
    LEFT JOIN ejl_clubs c ON (t.club_id = c.id)
    WHERE r.season = 2008
    GROUP BY p.id
    HAVING COUNT(DISTINCT c.id)  > 1
) p2 ON p1.id = p2.id
LEFT JOIN ejl_registration r1 ON ( r1.player_id = p1.id )
LEFT JOIN ejl_teams t1 ON ( r1.team_id = t1.id )
LEFT JOIN ejl_clubs c1 ON ( t1.club_id = c1.id )
WHERE  r1.season=2008

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

Редактировать: Следует отметить, что вы захотите использовать EXPLAIN в MySQL, чтобы убедиться, что этот запрос действительно выполняется более эффективно.

1 голос
/ 25 января 2009

Как я прокомментировал ваш вопрос на днях, вам не нужно использовать левое соединение в этом примере. Внешние объединения часто выполняются медленнее, чем внутренние, поэтому вы можете повысить производительность, используя простое внутреннее соединение.

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

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

SELECT p.surname, p.name, p.id, r.start_date, r.end_date, c1.short_name
FROM ejl_players p
 INNER JOIN ejl_registration r1 ON (r.player_id = p.id)
 INNER JOIN ejl_teams t1 ON (r.team_id = t1.id)
 INNER JOIN ejl_clubs c1 ON (t1.club_id = c1.id)
 INNER JOIN ejl_teams t2 ON (r.team_id = t2.id)
 INNER JOIN ejl_clubs c2 ON (t2.club_id = c2.id)
WHERE r.season = 2008
GROUP BY r.player_id, r.team_id
HAVING COUNT(DISTINCT c2.id) > 1;

Это работает в MySQL, потому что MySQL допускает правило с одним значением. То есть столбцы в предложении GROUP BY не обязательно должны совпадать с неагрегированными столбцами, указанными в вашем списке выбора. В других брендах СУБД этот запрос приведет к ошибке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...