Оптимизация запросов MySQL - PullRequest
0 голосов
/ 02 марта 2011

Не могли бы вы помочь мне оптимизировать этот запрос.Я потратил много времени и до сих пор не могу перефразировать его, чтобы он был достаточно быстрым (скажем, работает за считанные секунды, а не минуты, как сейчас).

Запрос:

SELECT m.my_id, m.my_value, m.my_timestamp
  FROM (
    SELECT my_id, MAX(my_timestamp) AS most_recent_timestamp
      FROM my_table
      WHERE my_timestamp < '2011-03-01 08:00:00'
      GROUP BY my_id
  ) as tmp
LEFT OUTER JOIN my_table m
ON tmp.my_id = m.my_id AND tmp.most_recent_timestamp = m.my_timestamp
ORDER BY m.my_timestamp;

my_table определяется следующим образом:

CREATE TABLE my_table (
   my_id INTEGER NOT NULL,
   my_value VARCHAR(4000),
   my_timestamp TIMESTAMP default CURRENT_TIMESTAMP NOT NULL,
   INDEX MY_ID_IDX (my_id),
   INDEX MY_TIMESTAMP_IDX (my_timestamp),
   INDEX MY_ID_MY_TIMESTAMP_IDX (my_id, my_timestamp)
);

Цель этого запроса - выбрать самый последний my_value для каждого my_id до некоторой отметки времени.my_table содержит ~ 100 миллионов записей, и для его выполнения требуется ~ 8 минут.

объяснение:

+----+-------------+-------------+-------+------------------------------------------------+-------------------------+---------+---------------------------+-------+---------------------------------------+
| id | select_type | table       | type  | possible_keys                                  | key                     | key_len | ref                       | rows  | Extra                                 |
+----+-------------+-------------+-------+------------------------------------------------+-------------------------+---------+---------------------------+-------+---------------------------------------+
|  1 | PRIMARY     | <derived2>  | ALL   | NULL                                           | NULL                    | NULL    | NULL                      | 90721 | Using temporary; Using filesort       |
|  1 | PRIMARY     | m          | ref   | MY_ID_IDX,MY_TIMESTAMP_IDX,MY_ID_TIMESTAMP_IDX | MY_TIMESTAMP_IDX        | 4       | tmp.most_recent_timestamp |    1  | Using where                           |
|  2 | DERIVED     | my_table    | range | MY_TIMESTAMP_IDX                               | MY_ID_MY_TIMESTAMP_IDX  | 8       | NULL                      | 61337 | Using where; Using index for group-by |
+----+-------------+-------------+-------+------------------------------------------------+-----------------------+---------+---------------------------+------+---------------------------------------+

Ответы [ 3 ]

2 голосов
/ 02 марта 2011

Если я правильно понимаю, вы сможете полностью отказаться от вложенного выбора и переместить предложение where в основной запрос, упорядочить по убыванию my_timestamp и ограничить 1.

SELECT my_id, my_value, max(my_timestamp)
FROM my_table
WHERE my_timestamp < '2011-03-01 08:00:00'
GROUP BY my_id

* edit - добавленомакс и сгруппировать по

1 голос
/ 02 марта 2011

хитрость для получения самой последней записи может заключаться в использовании order by вместе с 'limit 1' вместо max aggregation вместе с "self" join

что-то вроде этого (не проверено):

SELECT m.my_id, m.my_value, m.my_timestamp
FROM my_table m
WHERE my_timestamp < '2011-03-01 08:00:00'
ORDER BY m.my_timestamp DESC
LIMIT 1
;

обновление выше не работает, потому что требуется группировка ...
другое решение, которое использует WHERE-IN-SubSelect вместо JOIN, который вы использовали.
может быть быстрее. пожалуйста, проверьте ваши данные.

SELECT m.my_id, m.my_value, m.my_timestamp
FROM my_table m
WHERE ( m.my_id, m.my_timestamp ) IN (
  SELECT i.my_id, MAX(i.my_timestamp)
  FROM my_table i
  WHERE i.my_timestamp < '2011-03-01 08:00:00'
  GROUP BY i.my_id
  )
ORDER BY m.my_timestamp;
0 голосов
/ 02 марта 2011

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

Возможно, вы сможете ускорить его с помощью подсказки индекса.Я также обновил предложение ON для ссылки на tmp.most_recent_timestamp, используя его псевдоним.

SELECT m.my_id, m.my_value, m.my_timestamp
  FROM (
    SELECT my_id, MAX(my_timestamp) AS most_recent_timestamp
      FROM my_table
      WHERE my_timestamp < '2011-03-01 08:00:00'
      GROUP BY my_id
  ) as tmp
LEFT OUTER JOIN my_table m use index (MY_ID_MY_TIMESTAMP_IDX)
ON tmp.my_id = m.my_id AND tmp.most_recent_timestamp = m.my_timestamp
ORDER BY m.my_timestamp;
...