MySQL: проблема оптимизации подзапроса, при которой подзапрос проверяет более 14000 строк

#mysql #subquery #query-optimization #query-performance #groupwise-maximum

#mysql #подзапрос #оптимизация запроса #запрос-производительность #по группам — максимум

Вопрос:

Мне нужна помощь для оптимизации подзапроса ниже. Короче говоря, у меня есть следующий запрос, в котором tree таблица объединяет branch таблицу с s_id И максимальным timestamp количеством branch таблиц на основе условия подзапроса.

Я доволен результатом, который возвращает этот запрос. Однако этот запрос выполняется очень медленно. Узким местом является зависимый подзапрос ( branch2 ), который проверяет более 14000 строк. Как я могу оптимизировать подзапрос, чтобы ускорить этот запрос?

 SELECT *                
FROM dept.tree tree              
    LEFT JOIN dept.branch branch ON tree.s_id = branch.s_id                    
        AND branch.timestamp =
            (
                SELECT MAX(timestamp)                                   
                FROM dept.branch branch2 
                WHERE branch2.s_id = tree.s_id 
                AND branch2.timestamp <= tree.timestamp
            )                
WHERE tree.timestamp BETWEEN CONVERT_TZ('2020-05-16 00:00:00', 'America/Toronto', 'UTC') 
AND CONVERT_TZ('2020-05-16 23:59:59', 'America/Toronto', 'UTC') 
AND tree.s_id IN ('459','460')                
ORDER BY tree.timestamp ASC;
  

Дерево таблиц:

    id     box_id        timestamp       
373001645   1       2020-05-07 06:00:20 
373001695   1       2020-05-07 06:02:26
373001762   1       2020-05-07 06:05:17
373001794   1       2020-05-07 06:06:38
373001810   2       2020-05-07 06:07:21
  

Ветвь таблицы:

    id     box_id        timestamp                  data
373001345   1       2020-05-07 06:00:20     {"R": 0.114, "H": 20.808}
373001395   1       2020-05-07 06:02:26     {"R": 0.12, "H": 15.544}
373001462   1       2020-05-07 06:03:01     {"R": 0.006, "H": 55.469}
373001494   1       2020-05-07 06:04:38     {"R": 0.004, "H": 51.85}
373001496   1       2020-05-07 06:05:18     {"R": 0.02, "H": 5.8965}
373001497   1       2020-05-07 06:06:39     {"R": 0.12, "H": 54.32}
373001510   2       2020-05-07 06:07:09     {"R": 0.34, "H": 1.32}
373001511   2       2020-05-07 06:07:29     {"R": 0.56, "H": 32.7}
  

в ветке проиндексированы s_id и временная метка

Я использую версию 5.7.25-google-log

ОБЪЯСНЕНИЕ дает следующее:

     id    select_type        table     partitions    type    possible_keys    key    key_len    ref    rows    filtered     Extra
    1   PRIMARY tree        range   unique_timestamp_s_id,idx_s_id_timestamp,idx_timestamp  idx_s_id_timestamp  10      2629    100.00  Using index condition; Using filesort
    1   PRIMARY branch      ref unique_timestamp_s_id,idx_timestamp unique_timestamp_s_id   5   func    1   100.00  Using where
    2   DEPENDENT SUBQUERY  branch2     ref unique_timestamp_s_id,idx_s_id_timestamp,idx_timestamp  idx_s_id_timestamp  5   tree.s_id   14122   33.33   Using where; Using index
  

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

1. Примеры данных, желаемые результаты и объяснение логики, которую вы хотите реализовать, помогли бы.

2. Или просто подождите несколько минут

3. Какова точная версия MySQL?

4. Это «groupwise-max»?

5. Я добавил больше информации выше. Приношу свои извинения за то, что не добавил их раньше. Дайте мне знать, если я должен предоставить более подробную информацию. Спасибо

Ответ №1:

Это должно быть быстрее:

 select
    tree.s_id, tree.timestamp, branch.data
from
    (
        SELECT
            tree.s_id, tree.timestamp, max(branch.timestamp) as max_branch_timestamp
        FROM
            dept.tree tree
                LEFT JOIN dept.branch branch
                ON(
                        branch.s_id      =  tree.s_id
                    and branch.timestamp <= tree.timestamp
                )
        WHERE
            tree.timestamp BETWEEN
                CONVERT_TZ('2020-05-16 00:00:00', 'America/Toronto', 'UTC') AND
                CONVERT_TZ('2020-05-16 23:59:59', 'America/Toronto', 'UTC')
        AND tree.s_id IN ('459','460')                
        group by tree.s_id, tree.timestamp
    ) tree
        left outer join branch
        on(
                branch.s_id      = tree.s_id
            and branch.timestamp = tree.max_branch_timestamp
        )
  

Ответ №2:

Пожалуйста, предоставьте SHOW CREATE TABLE .

branch требуется INDEX(s_id, timestamp)

Вам нужно LEFT ? Возможно, это замедляет выполнение запроса без причины.

Комбинация IN в одном столбце и BETWEEN в другом может быть плохо оптимизирована; какую версию вы используете?

Пожалуйста, предоставьте EXPLAIN SELECT , чтобы мы могли обсудить, хорошо ли он оптимизирован. Если это не так, мы можем обсудить, как превратить IN (вариант на OR ) в UNION .

На самом деле это может быть быстрее, чем подходы, о которых я думал выше…

Имейте индекс выше, затем значительно перепишите запрос:

 SELECT b.*
    FROM ( SELECT  s_id,
                   MAX(timestamp) as timestamp                                
               FROM dept.branch 
               WHERE timestamp BETWEEN
                      CONVERT_TZ('2020-05-16 00:00:00', 'America/Toronto', 'UTC')
                  AND CONVERT_TZ('2020-05-16 23:59:59', 'America/Toronto', 'UTC')
                 AND s_id IN ('459','460')                
         ) AS x
    JOIN dept.branch AS b  USING(s_id, timestamp)
  

Сначала посмотрите, получает ли это правильную информацию. Затем я объясню, как сделать UNION в подзапросе (если вам нужна помощь).

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

1. Я предоставил больше информации выше, дайте мне знать, если это поможет. Приношу свои извинения за то, что не предоставил ранее. У меня есть вопрос, касающийся вашего запроса, вы, мужчины, присоединяетесь к dept.tree вместо dept.branch?

2. Приведенный выше запрос выдает следующую ошибку: Код ошибки: 1140. В агрегированном запросе без GROUP BY выражение # 1 списка ВЫБОРА содержит неагрегированный столбец ‘dept.s_id’; это несовместимо с sql_mode=only_full_group_by

3. Упс. Мне нужно взглянуть на это еще раз