Запустите запрос с LIMIT / OFFSET, а также получите общее количество строк

90

Для целей постраничной, мне нужно выполнить запрос , с LIMITи OFFSETстатей. Но я также нужен подсчет числа строк , которые будут возвращены этим запросом без LIMITи OFFSETстатей.

Я хочу бежать:

SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?

А также:

SELECT COUNT(*) FROM table WHERE /* whatever */

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

Тим
источник

Ответы:

168

Да. С простой оконной функцией:

SELECT *, count(*) OVER() AS full_count
FROM   tbl
WHERE  /* whatever */
ORDER  BY col1
OFFSET ?
LIMIT  ?

Имейте в виду, что стоимость будет значительно выше, чем без общего числа, но обычно все же дешевле, чем два отдельных запроса. Postgres должен фактически подсчитывать все строки в любом случае, что требует затрат в зависимости от общего количества подходящих строк. Детали:

Однако , как указала Дэни , когда OFFSETколичество строк, по крайней мере , равно количеству строк, возвращаемых из базового запроса, строки не возвращаются. Так что мы тоже не получаем full_count.

Если это неприемлемо, возможный обходной путь, чтобы всегда возвращать полный счет, был бы с CTE и OUTER JOIN:

WITH cte AS (
   SELECT *
   FROM   tbl
   WHERE  /* whatever */
   )
SELECT *
FROM  (
   TABLE  cte
   ORDER  BY col1
   LIMIT  ?
   OFFSET ?
   ) sub
RIGHT  JOIN (SELECT count(*) FROM cte) c(full_count) ON true;

Вы получаете одну строку значений NULL с full_countдобавлением, если OFFSETоно слишком велико. В противном случае он добавляется к каждой строке, как в первом запросе.

Если строка со всеми значениями NULL является возможным допустимым результатом, вы должны проверить, offset >= full_countчтобы устранить неоднозначность происхождения пустой строки.

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

Если доступны индексы, поддерживающие окончательный порядок сортировки, может быть полезно включить ORDER BYв CTE (избыточно).

Эрвин Брандштеттер
источник
3
И по LIMIT, и по условиям у нас есть строки, которые нужно вернуть, но с заданным смещением он не вернет результата. В этой ситуации, как мы сможем получить количество строк?
Дэни Мэтью
очень хорошо, спасибо, отлично работает, когда вы используете разбиение на страницы, таблицы данных, просто добавьте это в начало вашего sql и используйте его, сохраните дополнительный запрос для общего количества.
Ахмед Санни
Не могли бы вы подробнее рассказать об этом, если бы подсчет можно было динамически включить в запросе через входной параметр? У меня есть аналогичное требование, но пользователь решает, хочет ли он встроенного подсчета или нет.
julealgon
1
@julealgon: Пожалуйста, начните новый вопрос с определяющими деталями. Вы всегда можете ссылаться на это для контекста и оставить здесь комментарий, чтобы сделать обратную ссылку (и привлечь мое внимание), если хотите.
Эрвин Брандштеттер
1
@JustinL .: Дополнительные накладные расходы должны быть значительными только для относительно дешевых базовых запросов. Кроме того, Postgres 12 улучшил производительность CTE несколькими способами. (Хотя этот CTE по-прежнему используется MATERIALIZEDпо умолчанию, на него дважды ссылаются.)
Эрвин Брандштеттер,
1

edit: этот ответ действителен при получении неотфильтрованной таблицы. Я позволю, если это может кому-то помочь, но может не совсем ответить на первоначальный вопрос.

Ответ Эрвина Брандштеттера идеален, если вам нужно точное значение. Однако для больших таблиц часто требуется только довольно хорошее приближение. Postgres дает вам именно это, и он будет намного быстрее, поскольку ему не нужно будет оценивать каждую строку:

SELECT *
FROM (
    SELECT *
    FROM tbl
    WHERE /* something */
    ORDER BY /* something */
    OFFSET ?
    LIMIT ?
    ) data
RIGHT JOIN (SELECT reltuples FROM pg_class WHERE relname = 'tbl') pg_count(total_count) ON true;

На самом деле я совершенно не уверен, есть ли преимущество в использовании экстернализации RIGHT JOINили как в стандартном запросе. Это заслуживает некоторого тестирования.

SELECT t.*, pgc.reltuples AS total_count
FROM tbl as t
RIGHT JOIN pg_class pgc ON pgc.relname = 'tbl'
WHERE /* something */
ORDER BY /* something */
OFFSET ?
LIMIT ?
Франсуа Геген
источник
2
О быстрой оценке подсчета: stackoverflow.com/a/7945274/939860 Как вы сказали: действительно при получении всей таблицы, что противоречит WHEREпредложению в ваших запросах. Второй запрос логически неверен (извлекает по одной строке для каждой таблицы в БД) - и более затратный при исправлении.
Эрвин Брандштеттер,
-7

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

Лучше, вы можете использовать SQL_CALC_FOUND_ROWSв запросе, который сообщит MySQL получить общее количество строк вместе с результатами запроса ограничения.

Пример установлен как:

SELECT SQL_CALC_FOUND_ROWS employeeName, phoneNumber FROM employee WHERE employeeName LIKE 'a%' LIMIT 10;

SELECT FOUND_ROWS();

В приведенном выше SQL_CALC_FOUND_ROWSзапросе просто добавьте параметр в оставшийся требуемый запрос и выполните вторую строку, т.е. SELECT FOUND_ROWS()вернет количество строк в наборе результатов, возвращенном этим оператором.

Мохд Рашид
источник
1
Для решения требуется postgres, а не mysql.
MuffinMan
@MuffinMan, вы можете использовать то же самое в mysql. Начиная с MYSQL 4.0, в запросе используется опция SQL_CALC_FOUND_ROWS. Но из MYSQL 8.0 он устарел.
Мохд Рашид
Не имеет значения. На этот вопрос был дан ответ много лет назад. Если вы хотите внести свой вклад, опубликуйте новый вопрос с той же темой, но относящийся к MySQL.
MuffinMan
всегда быть актуальным
Али Хусейн
-14

Нет.

Возможно, есть небольшая выгода, которую вы теоретически можете получить, управляя ими по отдельности с достаточно сложным оборудованием под капотом. Но если вы хотите знать, сколько строк соответствует условию, вам придется подсчитать их, а не только ограниченное подмножество.

Ричард Хакстон
источник