より良いクエリ
手始めに、構文を修正し、かなり単純化して明確にすることができます:
SELECT *
FROM (
SELECT p.person_id, p.name, p.team, sum(s.score)::int AS score
,rank() OVER (PARTITION BY p.team
ORDER BY sum(s.score) DESC)::int AS rnk
FROM person p
JOIN score s USING (person_id)
GROUP BY 1
) sub
WHERE rnk < 3;
-
更新されたテーブルレイアウトに基づいて構築します。以下のフィドルを参照してください。
-
追加のサブクエリは必要ありません。ウィンドウ関数は後に実行されます 集計関数なので、デモンストレーションのようにネストできます。
-
「ランク」について話しているときは、おそらく
rank()
を使用することをお勧めします。 、row_number()
ではありません 。 -
people.people_id
を想定 はPKであり、GROUP BY
を簡略化できます 。 -
あいまいな可能性のあるすべての列名を必ずテーブル修飾してください
PL/pgSQL関数
次に、変数部分のパラメーターを受け取るplpgsql関数を記述します。a
の実装 -c
あなたのポイントの。 d
不明であり、追加する必要があります。
CREATE OR REPLACE FUNCTION f_demo(_agg text DEFAULT 'sum'
, _left_join bool DEFAULT FALSE
, _where_name text DEFAULT NULL)
RETURNS TABLE(person_id int, name text, team text, score int, rnk int) AS
$func$
DECLARE
_agg_op CONSTANT text[] := '{count, sum, avg}'; -- allowed functions
_sql text;
BEGIN
-- assert --
IF _agg ILIKE ANY (_agg_op) THEN
-- all good
ELSE
RAISE EXCEPTION '_agg must be one of %', _agg_op;
END IF;
-- query --
_sql := format('
SELECT *
FROM (
SELECT p.person_id, p.name, p.team, %1$s(s.score)::int AS score
,rank() OVER (PARTITION BY p.team
ORDER BY %1$s(s.score) DESC)::int AS rnk
FROM person p
%2$s score s USING (person_id)
%3$s
GROUP BY 1
) sub
WHERE rnk < 3
ORDER BY team, rnk'
, _agg
, CASE WHEN _left_join THEN 'LEFT JOIN' ELSE 'JOIN' END
, CASE WHEN _where_name <> '' THEN 'WHERE p.name LIKE $1' ELSE '' END
);
-- debug -- quote when tested ok
-- RAISE NOTICE '%', _sql;
-- execute -- unquote when tested ok
RETURN QUERY EXECUTE _sql
USING _where_name; -- $1
END
$func$ LANGUAGE plpgsql;
電話:
SELECT * FROM f_demo();
SELECT * FROM f_demo('sum', TRUE, '%2');
SELECT * FROM f_demo('avg', FALSE);
SELECT * FROM f_demo(_where_name := '%1_'); -- named param
-
PL/pgSQLをしっかりと理解する必要があります。そうでなければ、説明することが多すぎます。関連する回答は、SOのplpgsql にあります。 答えの事実上すべての詳細について。
-
すべてのパラメーターは安全に処理され、SQLインジェクションは不可能です。詳細:
-
特に、
WHERE
句は条件付きで追加されます(_where_name
の場合) 渡されます)位置パラメータ$1
クエリの刺し傷で。値はEXECUTE
に渡されます 値としてUSING
条項 。型変換、エスケープ、SQLインジェクションの機会はありません。例: -
DEFAULT
を使用する 関数パラメーターの値なので、自由に指定することも、指定しないこともできます。詳細: -
関数
format()
複雑な動的SQL文字列を安全かつクリーンな方法で構築するのに役立ちます。