[英]Paginate grouped query results with limit per page
我有一个项目表: [id, name, category_id]
和查询表达式: name LIKE '%Smi%'
每页限制为100
。
查询结果应按类别分组,并且每个页面可以显示一个或多个包含项目的类别。
每个页面包含一个或多个类别组(包含项目),但是单个页面上所有类别中所有项目的总数不能大于per_page
(100),但不能大于一个类别。
类别可能不会在多个页面之间细分。
没有类别ID( null
)的项目也应显示在组的中间。
结论:如何分组分组,限制分组总数?
我发现没有一个查询的解决方案,因为行之间存在某些依赖关系,这会导致递归问题。 这可能真的很残酷。 例如(对于每组最大行数== 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行)。 原因是简单的SUM不会计算第一页的一个空行(仅占用4行)。 因此,从理论上讲,我们需要存储下一步5与实际填充的行之间的差。 必须为下一行添加一个,如此。 因此,每一行的每个SUM递归取决于前几行的差异。 在一个简单的查询中确实很难做到。
我的解决方案提供了一个简单的命令式功能:
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
此函数才起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.