Лучший способ получить количество результатов до того, как было применено ОГРАНИЧЕНИЕ

StackOverflow https://stackoverflow.com/questions/156114

Вопрос

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

В настоящее время я делаю это, дважды запуская запрос, один раз завернутый в count() чтобы определить общие результаты, и второй раз с примененным ограничением, чтобы получить обратно только те результаты, которые мне нужны для текущей страницы.

Это кажется неэффективным.Есть ли лучший способ определить, сколько результатов было бы возвращено раньше LIMIT был применен?

Я использую PHP и Postgres.

Это было полезно?

Решение

Чистый SQL

С 2008 года многое изменилось.Вы можете использовать функция окна чтобы получить полное количество и ограниченный результат в одном запросе.(Знакомство с PostgreSQL 8.4 в 2009 году).

SELECT foo
     , count(*) OVER() AS full_count
FROM   bar
WHERE  <some condition>
ORDER  BY <some col>
LIMIT  <pagesize>
OFFSET <offset>

Обратите внимание, что это может быть значительно дороже, чем без учета общего количества.Все строки должны быть подсчитаны, и возможный короткий путь, берущий только верхние строки из соответствующего индекса, может больше не оказаться полезным.
Это не имеет большого значения при работе с маленькими столами или full_count <= OFFSET + LIMIT.Имеет значение для существенно большего full_count.

Угловой футляр:когда OFFSET по крайней мере, так же велико, как количество строк из базового запроса, никакого скандала возвращается.Таким образом, вы также не получаете full_count.Возможная альтернатива:

Рассмотрим следующие последовательность событий:

  1. WHERE оговорка (и JOIN условия, но не здесь) отфильтруйте соответствующие строки из базовой таблицы (таблиц).

    (GROUP BY и агрегатные функции пошли бы сюда.)

  2. Оконные функции применяются ко всем соответствующим строкам (в зависимости от OVER предложение и спецификация фрейма функции).Простой count(*) OVER() основан на всех строках.

  3. ORDER BY

    (DISTINCT или DISTINCT ON пошел бы сюда.)

  4. LIMIT / OFFSET применяются на основе установленного порядка выбора строк для возврата.

LIMIT / OFFSET становится все более неэффективным с увеличением числа строк в таблице.Рассмотрите альтернативные подходы, если вам нужна более высокая производительность:

Альтернативы для получения окончательного подсчета

Существуют совершенно разные подходы для получения количества затронутых строк (нет полный отсчет до OFFSET & LIMIT были применены).Postgres имеет внутреннюю бухгалтерию, на сколько строк повлияла последняя команда SQL.Некоторые клиенты могут получить доступ к этой информации или сами подсчитывать строки (например, psql).

Например, вы можете получить количество затронутых строк в plpgsql сразу после выполнения SQL-команды с:

GET DIAGNOSTICS integer_var = ROW_COUNT;

Подробности в руководстве пользователя.

Или вы можете использовать pg_num_rows в PHP.Или аналогичные функции в других клиентах.

Похожие:

Другие советы

Как я описываю в моем блоге, в MySQL есть функция , называемая SQL_CALC_FOUND_ROWS - поисковые системы.Это устраняет необходимость выполнять запрос дважды, но все равно необходимо выполнить запрос во всей его полноте, даже если предложение limit позволило бы остановить его раньше.

Насколько я знаю, подобной функции для PostgreSQL нет.Одна вещь, на которую следует обратить внимание при разбиении на страницы (наиболее распространенная вещь, для которой используется LIMIT, IMHO):выполнение "ПРЕДЕЛА СМЕЩЕНИЯ 1000 10" означает, что база данных должна извлекать по крайней мере 1010 строк, даже если это дает вам всего 10.Более эффективный способ сделать это - запомнить значение строки, по которой вы заказываете предыдущую строку (в данном случае 1000-ю), и переписать запрос следующим образом:"...ГДЕ order_row > значение_of_1000_th ПРЕДЕЛ 10".Преимущество заключается в том, что "order_row", скорее всего, проиндексирован (если нет, то у вас проблема).Недостатком является то, что если новые элементы добавляются между просмотрами страниц, это может немного не синхронизироваться (но опять же, это может быть незаметно для посетителей и может привести к значительному увеличению производительности).

Вы могли бы снизить снижение производительности, не выполняя запрос COUNT() каждый раз.Кэшируйте количество страниц, скажем, в течение 5 минут, прежде чем запрос будет запущен снова.Если только вы не видите огромное количество вставок, это должно работать просто отлично.

Поскольку Postgres уже выполняет определенное количество операций кэширования, этот метод не так неэффективен, как кажется.Это определенно не удваивает время выполнения.У нас есть таймеры, встроенные в наш уровень базы данных, так что я видел доказательства.

Поскольку вам нужно знать для целей подкачки, я бы предложил выполнить полный запрос один раз, записать данные на диск в виде кэша на стороне сервера, а затем передать их через ваш механизм подкачки.

Если вы запускаете запрос COUNT с целью принятия решения о том, предоставлять данные пользователю или нет (т. е.если есть > X записей, верните ошибку), вам нужно придерживаться подхода COUNT.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top