MySQL запрос выполняется очень медленно - PullRequest
0 голосов
/ 20 февраля 2020

У меня есть запрос MySQL (v5.7.26), который выполняется вечно. Вот запрос:

SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND ur.user_id IN (
        SELECT user_id
        FROM user_resource ur2
        WHERE ur2.action_date >= now() - INTERVAL 2 DAY
    )
    AND r.type = 'WORKOUT'
    GROUP BY ur.user_id;

Я немного поиграл с этим, пытаясь понять, в чем проблема. В целях тестирования я попытался разбить на две части. Итак:

SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY;

Возвращает (очень быстро) список user_id пользователя. Когда я подключаю возвращенный результат к первой части запроса, например:

SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND ur.user_id IN (1,1,1,4,4,5,6,7,7,7);
      AND r.type = 'WORKOUT'
GROUP BY ur.user_id

Он работает очень быстро. Мое предположение, что IN (подзапрос) является узким местом.

Я думал о том, чтобы извлечь подзапрос и получить user_ids, а затем использовать его как переменную, но я не уверен, что это хороший подход, и Кроме того, у меня есть проблемы с этим. это моя попытка:

-- first statement
SET @v1 = (SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)

-- second statement
SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND ur.user_id IN (@v1);
    AND r.type = 'WORKOUT'
GROUP BY ur.user_id

Проблема здесь в том, что первый оператор возвращает ошибку:

Подзапрос возвращает более 1 строки.

Ожидаемый результат - user_id, которые могут быть дубликатами. И мне нужны дубликаты для подсчета.

Как я могу это исправить?

Ответы [ 3 ]

0 голосов
/ 20 февраля 2020

Вы можете попробовать:

-- first statement
SET @v1 = (SELECT GROUP_CONCAT(user_id)
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)
-- second statement
SELECT
ur.user_id      AS user_id,
 sum(r.duration) AS total_time,
 count(user_id)  AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE ur.status = 1 AND NOT ur.action_date IS NULL AND FIND_IN_SET(ur.user_id,@v1)
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
0 голосов
/ 20 февраля 2020

Дополнительное объединение будет быстрее, чем подзапрос:

SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
INNER JOIN (
    SELECT user_id
    FROM user_resource ur2
    WHERE ur2.action_date >= now() - INTERVAL 2 DAY
) t ON t.user_id = ur.user_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND r.type = 'WORKOUT'
    GROUP BY ur.user_id;
0 голосов
/ 20 февраля 2020

Попробуйте EXISTS вместо IN

...
AND EXISTS (SELECT *
                   FROM user_resource ur2
                   WHERE ur2.user_id = ur.user_id
                         AND ur2.action_date >= now() - INTERVAL 2 DAY)
...

и индексы на user_resource (user_id, action_date), user_resource (status, action_date, user_id) и / или user_resource (type).

...