我自己反复遇到这个问题,并没有找到一个解决方案。
我最新的方法是将返回的 SET 的第一行定义为元数据。您的方法列表中还没有那个。该应用程序使用第一行进行簿记。实际数据从第二行开始。
明显的弱点:您必须处理必须压缩到元数据中的任何行定义。不过,一个简单的总数将适合任何数字或字符串类型。
当然,您必须对应用程序中的第一行进行特殊处理。
foo
这个简单的示例从定义为的表中返回行
CREATE TABLE foo (
foo_id serial PRIMARY KEY
,foo text
);
页面大小为 20 行,默认在函数标题中预设:
CREATE OR REPLACE FUNCTION f_paginate(_max_id int, _limit int = 20
, _offset int = 0)
RETURNS TABLE(foo_id int, foo text) AS
$BODY$
BEGIN
SELECT INTO foo_id count(*)::int
FROM foo f
WHERE f.foo_id < _max_id; -- get count
RETURN NEXT; -- use first row for meta-data
RETURN QUERY -- actual data starts with second row
SELECT f.foo_id, f.foo
FROM foo f
WHERE f.foo_id < _max_id
LIMIT _limit
OFFSET _offset;
END;
$BODY$
LANGUAGE plpgsql;
称呼:
SELECT * FROM f_paginate(100);
回报:
foo_id | foo
-------+----
86 | <NULL> <-- first row = meta-data
1 | bar <-- actual data
2 | baz
... 18 more ...
显然这种方法可以通过更高的_limit(页面大小)节省一些带宽。只有几行,几乎不值得开销。
另一种方法是您的“方法一” - 冗余添加一列:
CREATE OR REPLACE FUNCTION f_paginate2(_max_id int, _limit int = 20
, _offset int = 0)
RETURNS TABLE(foo_id int, foo text, ct bigint) AS
$BODY$
BEGIN
RETURN QUERY
SELECT f.foo_id, f.foo, count(*) OVER ()
FROM foo f
WHERE f.foo_id < _max_id
LIMIT _limit
OFFSET _offset;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
称呼:
SELECT * FROM f_paginate2(100);
回报:
foo_id | foo | ct
-------+-----+----
1 | bar | 86
2 | baz | 86
... 18 more ...
在这个简单的情况下,性能非常相似。第一个查询稍微快一点,但可能只是因为count(*) OVER ()
减慢了第二个查询。单独运行 justcount(*)
更快。