sql >> データベース >  >> RDS >> PostgreSQL

プログラムでSQLクエリを構築するための堅牢なアプローチ

    より良いクエリ

    手始めに、構文を修正し、かなり単純化して明確にすることができます:

    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
    

    SQLフィドル




    1. MariaDB Enterprise Clusterとは何ですか?

    2. SQL Plusから実行されているSQLスクリプトでアンパサンドを無視するにはどうすればよいですか?

    3. 値ではなく複数の列で並べ替えるにはどうすればよいですか?

    4. sql:v1とv2の間