Оптимизация запроса с помощью ORDER BY в столбце DATETIME - PullRequest
2 голосов
/ 23 марта 2020

Следующий запрос зависит от таблицы с ~ 4 тыс. Строк в таблице links и ~ 40 тыс. Строк в таблице comments и в настоящее время занимает около 0,2 с, что кажется довольно медленным, учитывая, что не много данных.

SELECT
    t1.id, t1.url, t1.dateAdded
FROM links AS t1 LEFT JOIN
comments AS t2
ON (t1.id = t2.linkId)
WHERE
    COALESCE(t2.dateAdded, t1.dateAdded) <= "2020-03-22 20:04:45"
GROUP BY t1.id
ORDER BY
    COALESCE(
        (
            SELECT
                MAX(dateAdded)
            FROM comments
            WHERE
                linkId = t1.id AND
                dateAdded <= "2020-03-22 20:04:45"
        ),
        t1.dateAdded
    ) DESC,
    t1.id DESC
    LIMIT 10

t1.id - первичный ключ, t2.linkId - внешний ключ; Я также попытался добавить индекс для dateAdded в обеих таблицах, но, похоже, это не помогло.

Просто, чтобы определить узкое место, я перебил запрос к следующему и заметил, что при упорядочении по t1.dateAdded запрос занимает 0,12 с, тогда как при упорядочении по t1.id он занимает всего 0,003 с

SELECT
    t1.id, t1.url, t1.dateAdded
FROM links AS t1 LEFT JOIN
comments AS t2
ON (t1.id = t2.linkId)
WHERE
    COALESCE(t2.dateAdded, t1.dateAdded) <= "2020-03-22 20:04:45"
GROUP BY t1.id
ORDER BY
    t1.id DESC -- here I tried both t1.dateAdded and t1.id

Поэтому я попытался найти разницу с помощью EXPLAIN, и, похоже, единственная разница заключается в Extra поле, где для ORDER BY t1.id оно пустое, а для ORDER BY t1.dateAdded это Using temporary; Using filesort (обратите внимание, что у меня индекс на t1.dateAdded). К сожалению, я застрял в интерпретации того, что это значит, и в целом, как оптимизировать исходный запрос. Обратите внимание, что id - это INT(10), а dateAdded - это DATETIME.

В целом, цель, которую я хотел бы достичь, - это упорядочить ссылки так, чтобы новейшие ссылки или ссылки с новыми комментариями вверху, где «новейший» означает относительно предоставленного времени (т.е. не учитывает ссылки / комментарии, добавленные после этого).

Заранее благодарен за любую помощь или подсказки

РЕДАКТИРОВАТЬ: Добавление более подробная информация

EXPLAIN для скрытого запроса с t1.id

+------+-------------+-------+-------+---------------+------------+---------+--------------+------+-------------+
| id   | select_type | table | type  | possible_keys | key        | key_len | ref          | rows | Extra       |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+-------------+
|    1 | SIMPLE      | t1    | index | NULL          | PRIMARY    | 4       | NULL         | 3674 |             |
|    1 | SIMPLE      | t2    | ref   | fk_link_id    | fk_link_id | 5       | db1.t1.id    |    8 | Using where |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+-------------+

EXPLAIN для скрытого запроса с t1.dateAdded

+------+-------------+-------+-------+---------------+------------+---------+--------------+------+---------------------------------+
| id   | select_type | table | type  | possible_keys | key        | key_len | ref          | rows | Extra                           |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+---------------------------------+
|    1 | SIMPLE      | t1    | index | NULL          | PRIMARY    | 4       | NULL         | 3674 | Using temporary; Using filesort |
|    1 | SIMPLE      | t2    | ref   | fk_link_id    | fk_link_id | 5       | db1.t1.id    |    8 | Using where                     |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+---------------------------------+

Информация о links таблица:

CREATE TABLE `links` (
  `id` int(10) UNSIGNED NOT NULL,
  `url` varchar(2083) CHARACTER SET utf8mb4 NOT NULL,
  `dateAdded` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `links`
  ADD PRIMARY KEY (`id`),
  ADD KEY `dateAdded` (`dateAdded`);

информация о comments таблица:

CREATE TABLE `comments` (
  `id` int(10) UNSIGNED NOT NULL,
  `linkId` int(10) UNSIGNED DEFAULT NULL,
  `userId` int(10) UNSIGNED NOT NULL,
  `content` varchar(2000) CHARACTER SET utf8mb4 NOT NULL,
  `dateAdded` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `comments`
  ADD PRIMARY KEY (`id`),
  ADD KEY `fk_link_id` (`linkId`);

ALTER TABLE `comments`
  ADD CONSTRAINT `fk_link_id` FOREIGN KEY (`linkId`) REFERENCES `links` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

1 Ответ

2 голосов
/ 23 марта 2020

Я могу начать с указания, что GROUP BY в вашем запросе не является необходимым (хотя и не ошибочным), потому что вы не выбираете никаких агрегатов. Кроме того, я чувствую, что вы бы упростили свою жизнь, просто используя MAX() в качестве аналитической c функции, а затем упорядочивая ее. Рассмотрим эту версию:

WITH cte AS (
    SELECT t1.id, t1.url, t1.dateAdded,
        MAX(t2.dateAdded) OVER (PARTITION BY t1.id) maxDateAdded
    FROM links AS t1
    LEFT JOIN comments AS t2 ON t1.id = t2.linkId
    WHERE
        (t2.dateAdded IS NOT NULL AND t2.dateAdded <= '2020-03-22 20:04:45') OR
        (t2.dateAdded IS NULL AND t1.dateAdded <= '2020-03-22 20:04:45')
)

SELECT id, url, dateAdded
FROM cte
ORDER BY maxDateAdded DESC, t1.id DESC
LIMIT 10;

В этом ответе предполагается, что вы используете MySQL 8+. Его можно переписать для более ранних версий MySQL с небольшим усилием.

Что касается оптимизации вышеупомянутого запроса, могут помочь следующие индексы:

CREATE INDEX idx2 ON comments (linkID, dateAdded);
CREATE INDEX idx1 ON links (dateAdded, url, id);

Эти индексы, если используются , ускорит соединение, а также позволит быстро оценить вызов MAX. Обратите внимание, что я переписал предложение WHERE для сортировки, избегая вызова COALESCE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...