Основная проблема здесь заключается в том, что называется планом запроса на раннее прерывание. Вот нить хакеров pg sql, описывающая нечто подобное:
https://www.postgresql.org/message-id/541A2335.3060100%40agliodbs.com
Цитируя оттуда, именно поэтому планировщик использует часто-чрезвычайно медленное сканирование индекса, когда присутствует ORDER BY done DES C:
Как обычно, PostgreSQL значительно недооценивает n_distinct: он показывает chapters.user_id в 146 000 и отношение to_user_id: from_user_id как 1: 105 (в отличие от 1: 6, что о реальном соотношении). Это означает, что PostgreSQL считает, что может найти 20 строк в первых 2% индекса ... тогда как на самом деле ему нужно сканировать 50% индекса, чтобы найти их.
В вашем В этом случае планировщик полагает, что если он просто начнет проходить строки, упорядоченные по done des c (IOW, используя индекс review_done), он быстро найдет 4 строки с clicker_id = 28. Поскольку строки должны быть возвращены в порядке убывания «выполнено», он считает, что это сохранит шаг сортировки и будет быстрее, чем извлечение всех строк для кликера 28 и последующая сортировка. Учитывая реальное распределение строк, это часто может оказаться не так, требуя пропустить огромное количество строк, прежде чем найти 4 с clicker = 28.
Более общий способ обработки он должен использовать CTE (который в 9.6 по-прежнему остается ограничением оптимизации - это изменение в PG 12, FYI) для извлечения строк без упорядочения, а затем добавить ORDER BY во внешний запрос , Принимая во внимание, что выборка всех строк для пользователя, их сортировка и возвращение столько, сколько вам нужно, вполне разумны для вашего набора данных (даже кликера по 7 тыс. Строк не должно быть проблемой), вы можете помешать планировщику поверить в преждевременную отмену План будет самым быстрым, если в CTE не будет ORDER BY или LIMIT, что даст вам запрос типа , Однако я не уверен, есть ли название для этого шаблона.