Запрос SQL JOIN требует более 15 секунд для запуска

У меня есть довольно большой SQL-запрос для получения данных из нескольких таблиц базы данных. Я использую условие ON, чтобы проверить, всегда ли совпадают идентификаторы guild_id, а в некоторых случаях он также проверяет идентификатор пользователя.

Это мой запрос:

SELECT
    SUM( f.guild_id = 787672220503244800 AND f.winner_id LIKE '%841827102331240468%' ) AS guild_winner,
    SUM( f.winner_id LIKE '%841827102331240468%' ) AS win_sum,
    m.message_count,
    r.bypass_role_id,
    i.real_count,
    i.total_count,
    i.bonus_count,
    i.left_count 
FROM
    guild_finished_giveaways AS f
    JOIN guild_message_count AS m
    JOIN guild_role_settings AS r
    JOIN guild_invite_count AS i ON m.guild_id = f.guild_id 
    AND m.user_id = 841827102331240468 
    AND r.guild_id = f.guild_id 
    AND i.guild_id = f.guild_id 
    AND i.user_id = m.user_id

Но он работает довольно медленно, более 15 секунд. Не понимаю, зачем так долго.

Я понял, что если я удалю JOIN "guild_invite_count", это снова будет довольно быстро. У меня есть какая-то простая ошибка, которую я не вижу? Или в чем может быть дело?

Вы пользуетесь индексом?

executable 10.01.2023 15:47

Я понятия не имею об индексах. Я думал, что guild_id будет что-то вроде этого.

Razzer 10.01.2023 15:47

GROUP BY не нужно?

jarlh 10.01.2023 15:48

Ваша цепочка JOIN немного обратная. Почему у вас нет отдельного ON для каждого JOIN?

jarlh 10.01.2023 15:49

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

Neville Kuyt 10.01.2023 15:53

Проблема почти наверняка заключается в предложении «LIKE» с ведущим подстановочным знаком.

Neville Kuyt 10.01.2023 15:58

Соединения и агрегация могут быть немного сложными. Получаете ли вы еще один результат вашей агрегации в подзапросе перед присоединением?

jarlh 10.01.2023 15:58

Является ли f.winner_id символьным/varchar столбцом, а m.user_id целочисленным/числовым столбцом? Почему?

jarlh 10.01.2023 16:06

Winner_id — это столбец типа varchar, поскольку он может содержать несколько идентификаторов в таком формате: «123 123 123».

Razzer 10.01.2023 16:28

Как просили ранее, добавьте к своему вопросу вывод EXPLAIN для вашего запроса и определения вашей таблицы. Вывод SHOW INDEX FROM tbl для каждой таблицы также будет полезен. Тот факт, что вы можете выполнить этот запрос без получения ошибки, говорит о том, что вы отключили ONLY_FULL_GROUP_BY, что является плохой идеей, поскольку позволяет выполнять недетерминированные запросы.

nnichols 10.01.2023 17:14

Хранение сериализованных данных (user_id, разделенных пробелами) в столбце f.winner_id — действительно плохая идея. Вы должны разделить их на их собственную соединительную таблицу.

nnichols 10.01.2023 17:34

Пожалуйста, укажите SHOW CREATE TABLE для каждого стола. Я предполагаю, что идентификаторы BIGINT UNSIGNED? Вам действительно нужен LIKE?

Rick James 11.01.2023 02:12
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
Доступ AWS Java Lambda к экземпляру AWS RDS MySQL с помощью CDK
Доступ AWS Java Lambda к экземпляру AWS RDS MySQL с помощью CDK
В этой статье мы рассмотрим, как включить доступ Java Lambda к экземпляру AWS RDS MySQL.
Как установить LAMP Stack 1/2 на Azure Linux VM
Как установить LAMP Stack 1/2 на Azure Linux VM
В дополнение к нашему предыдущему сообщению о намерении Azure прекратить поддержку Azure Database для MySQL в качестве единого сервера после 16...
1
12
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

FROM guild_finished_giveaways AS f
  JOIN guild_message_count AS m ON m.guild_id = f.guild_id 
  JOIN guild_role_settings AS r ON r.guild_id = f.guild_id
  JOIN guild_invite_count AS i ON i.guild_id = f.guild_id 
      AND i.user_id = m.user_id
WHERE m.user_id = 841827102331240468 

Также более чем странно использовать SUM() или любую другую агрегатную функцию в том же запросе, что и неагрегированные значения без предложения GROUP BY.

Хотя я согласен с тем, что критерии соединения должны быть в соответствующем предложении on, неверно предполагать, что сервер будет создавать декартово произведение с запросом OP. Оптимизатор разберется с неправильно расставленными критериями.

nnichols 10.01.2023 16:21

Вы используете InnoDB?

На каждом столе есть PRIMARY KEY?

Это может помочь:

m:  PRIMARY KEY(user_id)  -- assuming that is unique in that table
f:  INDEX(guild_id,  winner_id)
r:  INDEX(guild_id,  bypass_role_id)
i:  INDEX(user_id,)

Похоже, что некоторые таблицы не должны быть отдельными — возможно, r, i, f можно объединить? (Мне нужно увидеть SHOW CREATE TABLE, чтобы сказать больше.)

НЕ иметь коммалиста в winner_id. Вместо этого создайте другую таблицу с одной строкой для каждого победителя в каждой игре (или любой другой победитель). Возможно, только для столбцов, таких как таблица сопоставления «многие ко многим».

Отметив, что выполнение, скорее всего, начнется с m, а затем перейдет к i, давайте улучшим предложение Джоэла:

FROM   guild_message_count      AS m
  JOIN guild_invite_count       AS i ON i.user_id  = m.user_id
  JOIN guild_finished_giveaways AS f ON f.guild_id = m.guild_id 
  JOIN guild_role_settings      AS r ON r.guild_id = m.guild_id
WHERE m.user_id = 841827102331240468 

Обратите внимание, что 3 стола соединены guild_id; но нужно только 2 =.

SUM без GROUP BY суммирует весь набор результатов (после JOINing). Но у вас есть 6 неагрегатов, поэтому вам нужно GROUP BY все 6.

Но это может привести к сильно завышенным суммам. Возможно, вам нужно сначала выполнить агрегацию чуть выше f, так как именно здесь вы суммируете. Тогда ПРИСОЕДИНЯЙТЕСЬ к остальным??

Другие вопросы по теме