Я использую Postgres 9.4.
Схема messages
имеет следующую схему: сообщения принадлежат feed_id, и имеет posts_at, также сообщения могут иметь родительское сообщение (в случае ответов).
Table "public.messages"
Column | Type | Modifiers
------------------------------+-----------------------------+-----------
message_id | character varying(255) | not null
feed_id | integer |
parent_id | character varying(255) |
posted_at | timestamp without time zone |
share_count | integer |
Indexes:
"messages_pkey" PRIMARY KEY, btree (message_id)
"index_messages_on_feed_id_posted_at" btree (feed_id, posted_at DESC NULLS LAST)
Я хочу вернуть все заказанные сообщения share_count
, но для каждого parent_id
я хочу вернуть только одно сообщение. т. е. если несколько сообщений имеют одинаковое значение parent_id
, posted_at
возвращается только последнее ( ). Значение parent_id
может быть нулевым, все сообщения с нулевым значением parent_id
должны возвращаться.
Запрос, который я использовал:
WITH filtered_messages AS (SELECT *
FROM messages
WHERE feed_id IN (7)
AND (posted_at >= '2015-01-01 04:00:00.000000')
AND (posted_at < '2015-04-28 04:00:00.000000'))
SELECT *
FROM (SELECT DISTINCT ON(COALESCE(parent_id, message_id)) parent_id,
message_id,
posted_at,
share_count
FROM filtered_messages
ORDER BY COALESCE(parent_id, message_id), posted_at DESC NULLS LAST
) messages
ORDER BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
Вот http://sqlfiddle.com/#!15/588e5/1/0 , в скрипте SQL я определил схему, точный запрос и ожидаемый результат.
Но производительность запроса замедляется, как только таблица сообщений становится большой. Я пытался добавить несколько индексов сортировки, но он, похоже, не использует индекс. Вот объяснение: http://explain.depesz.com/s/Sv2
Как я могу создать правильный индекс?
источник
ORDER BY
подзапрос совершенно бесполезен. Кроме того, связанный план не может быть результатом опубликованного запроса - например, нет упоминания о немmetadata
.feed_id
и ,posted_at
и вы не упоминалиmetadata
вообще, что , как представляется, типа JSON? Пожалуйста, исправьте свой вопрос, чтобы сделать его последовательным. Вы выбираете> 500 тыс. Строк в CTE ... Сколько строк в таблице? Какой процент строк вы обычно выбираете в CTE? Какой процент строк имеетparent_id IS NULL
? Посмотрите информацию в теге [postgresql-performance] для вопросов производительности.parent_id
? (мин. / ср. / макс.)metadata
. В настоящее время таблица сообщений содержит 10 мил данных, но быстро увеличивается. Я думаю, чтобы разделить на таблицы разделов для каждого feed_id. Так как я получаю только по идентификатору канала. процентное значение parent_id NULL против NULL составляет около 60% / 40%. типичная выборка составляет около 1-2% таблицы. (около 100K сообщений) Производительность для 100K составляет около 1 с, но когда он достигает 500K +, он использует индекс растрового изображения и обычно занимает 10 с.Ответы:
запрос
Этот запрос должен быть значительно быстрее в любом случае:
CTE здесь не делает ничего, что не мог бы доставить и простой подзапрос. И CTE вводит барьер оптимизации, поскольку он выполняется отдельно и его результат материализуется.
У вас есть еще один уровень подзапроса, чем вам на самом деле нужно.
Выражение
(COALESCE(parent_id, message_id)
несовместимо с простым индексом, вам понадобится индекс для этого выражения. Но это может быть не очень полезным, в зависимости от распределения данных. Следуйте моим ссылкам ниже для получения подробной информации.Разделение простого случая
parent_id IS NULL
на отдельныйSELECT
может дать или не дать оптимальное. Особенно нет, если это все-таки редкий случай, и в этом случае комбинированный запрос с индексом(COALESCE(parent_id, message_id)
может работать лучше. Другие соображения применимы ...индексы
Особенно когда поддерживается с этими индексами:
Два частичных индекса охватывают всю таблицу вместе и имеют примерно одинаковый размер вместе как один общий индекс.
Последние два столбца
parent_id, message_id
имеют смысл только в том случае, если вы получаете только сканирование по индексу . Еще удалите их из обоих индексов.SQL Fiddle.
В зависимости от недостающих деталей,
DISTINCT ON
может быть или не быть лучшим методом запроса для этой цели. Прочитайте подробное объяснение здесь:И, возможно, более быстрые альтернативы здесь:
источник