ОБЪЕДИНЕНИЕ ВСЕГО MYSQL действительно медленное

#mysql #sql #join #select #union-all

#mysql #sql #Присоединиться #выберите #объединение всех

Вопрос:

У меня есть запрос в mysql с 2 select from. Когда я запускаю эти запросы по отдельности, они выполняются быстро в течение 1 секунды. Но когда я объединяю их с union all, веб-сайт зависает, и для выполнения одного и того же запроса в union all требуется не менее 20 секунд.

Есть идеи, почему это происходит? Не могу понять.

Смотрите Запрос ниже:

 SELECT p.user_id, p.description, p.post_id, p.created_at, ps.username AS size_name, ps.username AS user_name, ps.avatar AS avatar, pz.title AS title, pz.slug AS slug 
FROM comments p 
JOIN users ps ON ps.id = p.user_id 
JOIN posts pz ON pz.id = p.post_id 

UNION ALL

SELECT p2.user_id, p2.description, p2.post_id, p2.created_at, ps2.username AS size_name, ps2.username AS user_name, ps2.avatar AS avatar, pz2.title AS title, pz2.slug AS slug 
FROM reply p2 
JOIN users ps2 ON ps2.id = p2.user_id 
JOIN posts pz2 ON pz2.id = p2.post_id 

order by created_at DESC 
LIMIT 10
 

Комментарии:

1. Сколько строк возвращает каждый из подзапросов?

2. @Barmar около 2 м каждый.

3. Сначала СДЕЛАЙТЕ ЗАКАЗ и ОГРАНИЧЬТЕ 10 в каждом ВЫБОРЕ. Затем сделайте это и для результата ОБЪЕДИНЕНИЯ ВСЕХ.

4. Ваша проблема заключается в предложении ORDER BY. ОБЪЕДИНЕНИЮ необходимо получить все записи, а затем отсортировать их.

Ответ №1:

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

 SELECT *
FROM (
    (SELECT p.user_id, p.description, p.post_id, p.created_at, ps.username AS size_name, ps.username AS user_name, ps.avatar AS avatar, pz.title AS title, pz.slug AS slug 
    FROM comments p 
    JOIN users ps ON ps.id = p.user_id 
    JOIN posts pz ON pz.id = p.post_id 
    ORDER BY created_at DESC
    LIMIT 10) 
    
    UNION ALL
    
    (
    SELECT p2.user_id, p2.description, p2.post_id, p2.created_at, ps2.username AS size_name, ps2.username AS user_name, ps2.avatar AS avatar, pz2.title AS title, pz2.slug AS slug 
    FROM reply p2 
    JOIN users ps2 ON ps2.id = p2.user_id 
    JOIN posts pz2 ON pz2.id = p2.post_id 
    ORDER BY created_at DESC
    LIMIT 10)
) AS x
order by created_at DESC 
LIMIT 10
 

Комментарии:

1. вы можете просто использовать круглые скобки, а не подзапрос, чтобы иметь порядок по / limit для отдельных запросов; см. Мой ответ

2. Но вам нужен внешний select, чтобы отфильтровать его до 10 лучших объединенных запросов.

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

4. о, не знал, что вы можете сделать это так, как вы показываете.

Ответ №2:

ОБЪЕДИНЕНИЕ по умолчанию имеет значение UNION DISTINCT . Если вы хотите ОБЪЕДИНИТЬ ВСЕ, прямо скажите это (и вы должны, где это возможно, поскольку UNION DISTINCT должен проделать большую работу, чтобы сделать их разными).

Другое дело, что порядок и ограничение применяются к запросу в целом. Возможно, вы захотите, чтобы в каждом объединенном запросе они тоже были. Смотрите https://dev.mysql.com/doc/refman/8.0/en/union.html:

ПОРЯДОК и ОГРАНИЧЕНИЕ в объединениях

Чтобы применить предложение ORDER BY или LIMIT к отдельному SELECT, заключите SELECT в круглые скобки и поместите предложение в круглые скобки:

 (SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
 

Если в ваших быстрых отдельных версиях они были, это должно помочь. Итак, что-то вроде этого:

 (SELECT p.user_id, p.description, p.post_id, p.created_at, ps.username AS size_name, ps.username AS user_name, ps.avatar AS avatar, pz.title AS title, pz.slug AS slug 
FROM comments p 
JOIN users ps ON ps.id = p.user_id 
JOIN posts pz ON pz.id = p.post_id 
order by created_at DESC 
LIMIT 10)

UNION ALL

(SELECT p2.user_id, p2.description, p2.post_id, p2.created_at, ps2.username AS size_name, ps2.username AS user_name, ps2.avatar AS avatar, pz2.title AS title, pz2.slug AS slug 
FROM reply p2 
JOIN users ps2 ON ps2.id = p2.user_id 
JOIN posts pz2 ON pz2.id = p2.post_id 
order by created_at DESC 
LIMIT 10)

order by created_at DESC 
LIMIT 10
 

Комментарии:

1. Я забыл добавить ОБЪЕДИНЕНИЕ ALL здесь, в stackoverflow. Я изменил его. Спасибо.

2. Этот запрос вернет только 2 строки; 10 и 11.

3. Это вернет до 20 строк.

4. @Barmar, первый ВЫБОР вернет до 10 строк, все значения 10. Второй ВЫБОР вернет до 10 строк, все значения 11. ОБЪЕДИНЕНИЕ удалит дубликаты и вернет только одно 10 и одно 11 (если оба значения существуют.)

5. Это не соответствует исходному запросу, поскольку им нужны верхние 10 строк из двух объединенных запросов.