Сгруппировать результаты запроса с ограничением на страницу - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть таблица элементов: [id, name, category_id]

И выражение запроса: name LIKE '%Smi%'

Предел на страницу составляет 100.

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

Каждая страница содержит одну или несколько групп категорий (с элементами), но общее количество всех элементов во всех категориях на одной странице не можетбыть больше per_page (100), но как минимум на одну категорию.

Категории не могут быть разбиты на несколько страниц.

Элементы без идентификаторов категорий (null) также должны отображаться в середине групп.

Заключение: Как разделить на группы, ограничения по общему количеству элементов в группе?

1 Ответ

0 голосов
/ 19 декабря 2018

Я не нашел решения для одного запроса, потому что между строками есть определенные зависимости, которые приводят к рекурсивным проблемам.Это может быть очень жестоко.Например (для макс. Строк на группу == 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 параметра:

  1. максимальное количество строк на странице
  2. индекс интересующей вас страницы
  3. текст фильтра name

Объяснение :

  1. Этот запрос вычисляет количество строк на категорию.Результат будет повторяться в пределах LOOP:
  2. . _remainder хранит значение того, сколько строк уже помещается на текущей странице.Если в текущей категории больше строк, чем позволяет остаток, генерируется новая страница (_page_counter увеличено), а остаток будет сброшен.
  3. Если _page_counter выше, чем интересующий _page_id, то не дальшенеобходимы вычисления
  4. Остаток будет уменьшен на количество строк текущей категории
  5. Если _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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...