PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Solidne podejście do programowego budowania zapytań SQL

Lepsze zapytanie

Na początek możesz poprawić składnię, uprościć i nieco wyjaśnić:

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;
  • Opierając się na moim zaktualizowanym układzie tabeli. Zobacz skrzypce poniżej.

  • Nie potrzebujesz dodatkowego podzapytania. Funkcje okna są wykonywane po funkcje agregujące, dzięki czemu można je zagnieżdżać, jak pokazano.

  • Mówiąc o „rankingu”, prawdopodobnie chcesz użyć rank() , a nie row_number() .

  • Zakładając people.people_id jest PK, możesz uprościć GROUP BY .

  • Pamiętaj, aby zakwalifikować do tabeli wszystkie nazwy kolumn, które mogą być niejednoznaczne

Funkcja PL/pgSQL

Następnie napisałbym funkcję plpgsql, która pobiera parametry dla twoich zmiennych części. Implementacja a - c swoich punktów. d jest niejasne, pozostawiając to do dodania.

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;

Zadzwoń:

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

Skrzypce SQL




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Znajdź wszystkie wiersze używając jakiegoś zakresu Unicode (takiego jak znaki cyrylicy) za pomocą PostgreSQL?

  2. Heroku nie migruje modeli w Django

  3. Odfiltrowywanie zduplikowanych kolejnych rekordów w SELECT

  4. Jak uzyskać dynamiczny widok 12 dni roboczych w Postgresql?

  5. Jak odpytywać postgres o opcjonalne parametry?