sql >> Databáze >  >> RDS >> PostgreSQL

Robustní přístup pro vytváření SQL dotazů programově

Lepší dotaz

Pro začátek můžete opravit syntaxi, zjednodušit a ujasnit docela dost:

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;
  • Vychází z mého aktualizovaného rozložení tabulky. Viz housle níže.

  • Nepotřebujete další poddotaz. Funkce okna se provádějí po agregační funkce, takže je můžete vnořit, jak je ukázáno.

  • Když mluvíme o „hodnocení“, pravděpodobně budete chtít použít rank() , nikoli row_number() .

  • Za předpokladu people.people_id je PK, můžete zjednodušit GROUP BY .

  • Ujistěte se, že jste kvalifikovali všechny názvy sloupců, které mohou být nejednoznačné

Funkce PL/pgSQL

Pak bych napsal funkci plpgsql, která přebírá parametry pro vaše proměnné části. Implementace a - c z vašich bodů. d je nejasné, takže to můžete přidat.

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;

Volejte:

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 Fiddle




  1. PostgreSQL:Vytvořte index pro booleovský sloupec

  2. Rozdíl mezi mysql, mysqladmin, mysqld

  3. oci_bind_by_name nefunguje v PHP

  4. Spring-boot webová aplikace po chvíli ztrácí schopnost připojit se k MySQL / RDS