私は自分でこの問題に繰り返し遭遇しましたが、1つの解決策を見つけられませんでした。
私の最新のアプローチは、返されたSETの最初の行をメタデータとして定義することでした。あなたはまだアプローチのリストにそれを持っていません。アプリケーションは最初の行を簿記に使用します。実際のデータは2行目から始まります。
明らかな弱点:メタデータに詰め込む必要のある行定義をすべて処理する必要があります。ただし、単純な合計数は、任意の数値または文字列タイプに適合します。
もちろん、アプリケーションの最初の行を特殊なケースにする必要があります。
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(ページサイズ)で帯域幅を節約できます。数行しかないため、オーバーヘッドの価値はほとんどありません。
別のアプローチは「アプローチ1」です-列を冗長に追加します:
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 ()
2番目のクエリの速度が低下するためです。だけで個別に実行するcount(*)
方が高速です。