Я не нашел решения для одного запроса, потому что между строками есть определенные зависимости, которые приводят к рекурсивным проблемам.Это может быть очень жестоко.Например (для макс. Строк на группу == 5):
CATEGORY_ID | NUMBER OF ROWS
------------+----------------
1 | 4
2 | 3
3 | 2
4 | 1
Если бы я просто добавил столбцы, я бы получил 4 для первой строки.Это его собственная страница.Следующим будет 7 рядов (4 + 3).7 больше 5, новая страница.Теперь у меня будет 9 (4 + 3 + 2).Та же категория, что и раньше.И затем я получу 10. Обычно следующая страница будет сгенерирована в 11. Таким образом, 4-я категория будет перенесена на ту же страницу, что и 2 и 3 (что, конечно, не подходит, потому что это 6 строк).Причина в том, что простая сумма не учитывает одну пустую строку первой страницы (где были взяты только 4 строки).Таким образом, в теории нам нужно сохранить разницу между следующим шагом 5 и фактически заполненными строками.Это должно быть добавлено для следующих строк и так далее.Таким образом, каждый СУММ каждой отдельной строки рекурсивно зависит от разницы предыдущих строк.Это очень трудно сделать в простом запросе.
Мое решение предлагает простую императивную функцию:
demo: db <> fiddle
CREATE OR REPLACE FUNCTION get_category_for_page(_max_rows int, _page_id int, _filter text) RETURNS int[] AS $$
DECLARE
_remainder int := _max_rows;
_page_counter int := 1;
_categories int[] = '{}';
_temprow record;
BEGIN
FOR _temprow IN
SELECT -- 1
category_id, count(*)
FROM categories
WHERE name LIKE _filter
GROUP BY category_id
ORDER BY category_id
LOOP
IF (_remainder - _temprow.count < 0) THEN -- 2
_page_counter := _page_counter + 1;
_remainder := _max_rows;
END IF;
IF (_page_counter > _page_id) THEN -- 3
EXIT;
END IF;
_remainder := _remainder - _temprow.count; -- 4
IF (_page_counter = _page_id) THEN -- 5
_categories := _categories || _temprow.category_id;
END IF;
END LOOP;
RETURN _categories;
END;
$$ LANGUAGE plpgsql;
Функция принимает 3 параметра:
- максимальное количество строк на странице
- индекс интересующей вас страницы
- текст фильтра
name
Объяснение :
- Этот запрос вычисляет количество строк на категорию.Результат будет повторяться в пределах
LOOP
: - .
_remainder
хранит значение того, сколько строк уже помещается на текущей странице.Если в текущей категории больше строк, чем позволяет остаток, генерируется новая страница (_page_counter
увеличено), а остаток будет сброшен. - Если
_page_counter
выше, чем интересующий _page_id
, то не дальшенеобходимы вычисления - Остаток будет уменьшен на количество строк текущей категории
- Если
_page_counter
равно интересному _page_id
, текущая категория будет добавлена к выходным данным.Это может происходить несколько раз.
Теперь вы можете вызывать функцию следующим образом:
SELECT get_category_for_page(5, 1, '%A%');
Таким образом, ваш запрос будет выглядеть следующим образом:
SELECT
*
FROM categories
WHERE
category_id = ANY(get_category_for_page(5, 1, '%A%'))
AND name LIKE '%A%'
ORDER BY id
Отказ от ответственности :
Подумайте о _max_rows == 5
.Теперь ваша первая категория имеет 6 строк.Поскольку эта категория будет превышать максимальное количество строк на страницу, ее необходимо разделить, чтобы уместить на одной странице.Но ваше ограничение говорит, что категория не должна быть разделена.Так что нет определенного поведения для обработки этого особого случая.Таким образом, эта функция работает только в том случае, если количество строк в каждой отдельной категории меньше или равно _max_rows
.