PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Dinâmico ORDER BY e ASC/DESC em uma função plpgsql


Eu faria assim:
CREATE OR REPLACE FUNCTION list(
      _category varchar(100)
    , _limit int
    , _offset int
    , _order_by varchar(100)
    , _order_asc_desc text = 'ASC')  -- last param with default value
  RETURNS TABLE(id int, name varchar, clientname varchar, totalcount bigint)
  LANGUAGE plpgsql AS
$func$
DECLARE
   _empty text := '';
BEGIN
   -- Assert valid _order_asc_desc
   IF upper(_order_asc_desc) IN ('ASC', 'DESC', 'ASCENDING', 'DESCENDING') THEN
      -- proceed
   ELSE
      RAISE EXCEPTION 'Unexpected value for parameter _order_asc_desc.
                       Allowed: ASC, DESC, ASCENDING, DESCENDING. Default: ASC';
   END IF;
   
   RETURN QUERY EXECUTE format(
     'SELECT id, name, clientname, count(*) OVER() AS full_count
      FROM   design_list
      WHERE ($1 = $2 OR category ILIKE $1) 
      ORDER  BY %I %s
      LIMIT  %s
      OFFSET %s'
    , _order_by, _order_asc_desc, _limit, _offset)
   USING _category, _empty;
END
$func$;

Recurso principal:use o formato format() para concatenar com segurança e elegância sua string de consulta. Relacionado:

ASC / DESC (ou ASCENDING / DESCENDING ) são palavras-chave fixas. Adicionei uma verificação manual (IF ... ) e depois concatenar com um simples %s . Isso é um maneira de afirmar a entrada legal. Por conveniência, adicionei uma mensagem de erro para entrada inesperada e um parâmetro padrão, para que a função padrão seja ASC se o último parâmetro for omitido na chamada. Relacionado:

Abordando Pavel é válido comentar , eu concateno _limit e _offset diretamente, então a consulta já está planejada com esses parâmetros.

_limit e _offset são integer parâmetros, para que possamos usar %s simples sem o perigo de injeção de SQL. Você pode querer afirmar valores razoáveis ​​(excluir valores negativos e valores muito altos) antes de concatenar...
Outras notas:

  • Use uma convenção de nomenclatura consistente. Prefixei todos os parâmetros e variáveis ​​com um sublinhado _ , não apenas algumas .

  • Não usando qualificação de tabela dentro de EXECUTE , já que há apenas uma única tabela envolvida e o EXECUTE tem seu escopo separado.

  • Renomeei alguns parâmetros para esclarecer. _order_by em vez de _sort_by; _order_asc_desc em vez de _order .