テーブルと追加の列を返すPostgreSQL関数を書きたいのですが。 RETURNS TABLE (col1 type, col2 type, ...)
を手動で指定せずにこれを行う方法はありますか?
たとえば、次の関数について考えます。
CREATE FUNCTION get_users_with_most_videos_since_time(ts TIMESTAMPTZ)
RETURNS SETOF "user" AS $$
SELECT
u.*,
count(v.id) AS vids_since
FROM "user" AS u
INNER JOIN "video" AS v ON v.creator_id = u.id
WHERE v.created_at > ts
GROUP BY u.id
ORDER BY vids_since DESC;
$$ LANGUAGE SQL;
これはエラーで失敗します:
ERROR: return type mismatch in function declared to return "user" DETAIL: Final statement returns too many columns.
公平に言えば、vids_since
テーブルに存在しない"user"
列が含まれています。
これを修正するには、次のように変更します。
CREATE FUNCTION get_users_with_most_videos_since_time(ts TIMESTAMPTZ)
RETURNS
TABLE (<<< all columns from table "user" >>>, vids_since BIGINT)
AS $$
...
ここに"user"
テーブルのスキーマ全体を再コピーせずにこれを行う方法はありますか?
AIUI、あなたの望みは、関数のRETURNS
節を短くすることです。同時にテーブルの行タイプへの依存関係を確立する必要があるかどうかはわかりませんが、ここでも意味があります。
RETURNS SETOF
rettype
の形式は、システムカタログに格納される使用されたタイプに依存します。 マニュアル:
戻り値の型は、基本型、複合型、またはドメイン型であるか、テーブル列の型を参照できます。
したがって、必要な行タイプをシステムに登録するだけです(一度だけ)。 CREATE TYPE
を使用して複合型を明示的に作成できます。または、別のテーブル、ビュー、またはマテリアライズドビューを作成して、暗黙的に行うこともできます。一時的な機能のための一時的なビューまたはテーブルでさえ(セッションの最後で死にます)。 @ Abelistoがコメントしたように:
CREATE VIEW user_plus AS
SELECT *, null::bigint AS vids_since
FROM "user"
WHERE false;
ただし、SELECT *
はビューの作成時間で列のリストに解決されます( "早期バインディング")。後で基になるテーブルに列を追加しても、ビューとその行の型は更新されず、関数の実行時に別の型の不一致が発生します。ただし、基になるテーブルから列を削除しようとすると、その列に依存するビューの列に不満があります。そして、あなたは、ビュー(および関数)を変更します。
関数は簡略化され、高速化できます。
CREATE FUNCTION get_users_with_most_videos_since_time(_ts timestamptz)
RETURNS SETOF user_plus AS
$func$
SELECT * -- effectively the same as: u.*, v.vids_since
FROM "user" AS u
JOIN ( -- aggregate *before* you join
SELECT creator_id AS id, count(*) AS vids_since -- note the column alias
FROM video v
WHERE created_at > _ts
GROUP BY 1
) v USING (id) -- USING only retains one id column
ORDER BY v.vids_since DESC;
$func$ LANGUAGE SQL;
この関数を持つことは実際に意味があります。結果に含まれていない列でフィルタリングしたい。プレーンなVIEW
では不可能です。
余談:userのような予約語を識別子として使用することはお勧めしません。それはロードされたフットガンです。