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

Como retornar o resultado de um SELECT dentro de uma função no PostgreSQL?


Use RETURN QUERY :
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt   text   -- also visible as OUT parameter inside function
               , cnt   bigint
               , ratio bigint)
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.txt
        , count(*) AS cnt                 -- column alias only visible inside
        , (count(*) * 100) / _max_tokens  -- I added brackets
   FROM  (
      SELECT t.txt
      FROM   token t
      WHERE  t.chartype = 'ALPHABETIC'
      LIMIT  _max_tokens
      ) t
   GROUP  BY t.txt
   ORDER  BY cnt DESC;                    -- potential ambiguity 
END
$func$;

Ligar:
SELECT * FROM word_frequency(123);

Definir o tipo de retorno explicitamente é muito mais prático do que retornar um record genérico . Dessa forma, você não precisa fornecer uma lista de definição de coluna com cada chamada de função. RETURNS TABLE é uma maneira de fazer isso. Há outros. Tipos de dados de OUT parâmetros devem corresponder exatamente ao que é retornado pela consulta.

Escolha nomes para OUT parâmetros cuidadosamente. Eles são visíveis no corpo da função em quase qualquer lugar. Colunas qualificadas de tabela com o mesmo nome para evitar conflitos ou resultados inesperados. Eu fiz isso para todas as colunas no meu exemplo.

Mas observe o potencial conflito de nomenclatura entre o OUT parâmetro cnt e o alias de coluna com o mesmo nome. Neste caso particular (RETURN QUERY SELECT ... ) O Postgres usa o alias da coluna sobre o OUT parâmetro de qualquer maneira. Isso pode ser ambíguo em outros contextos, no entanto. Existem várias maneiras de evitar qualquer confusão:
  1. Use a posição ordinal do item na lista SELECT:ORDER BY 2 DESC . Exemplo:
    • Selecionar a primeira linha em cada grupo GROUP BY?
  2. Repita a expressão ORDER BY count(*) .
  3. (Não aplicável aqui.) Defina o parâmetro de configuração plpgsql.variable_conflict ou use o comando especial #variable_conflict error | use_variable | use_column na função. Veja:
    • Conflito de nomenclatura entre parâmetro de função e resultado de JOIN com cláusula USING

Não use "texto" ou "contagem" como nomes de coluna. Ambos são legais para usar no Postgres, mas "count" é uma palavra reservada no SQL padrão e um nome de função básico e "texto" é um tipo de dados básico. Pode levar a erros confusos. Eu uso txt e cnt nos meus exemplos, você pode querer nomes mais explícitos.

Adicionado um ; e corrigiu um erro de sintaxe no cabeçalho. (_max_tokens int) , não (int maxTokens) - tipo depois de nome .

Ao trabalhar com divisão inteira, é melhor multiplicar primeiro e dividir depois, para minimizar o erro de arredondamento. Ou trabalhe com numeric ou um tipo de ponto flutuante. Ver abaixo.

Alternativa


Isto é o que eu penso sua consulta deve se parecer com (calculando um compartilhamento relativo por token ):
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt            text
               , abs_cnt        bigint
               , relative_share numeric)
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.txt, t.cnt
        , round((t.cnt * 100) / (sum(t.cnt) OVER ()), 2)  -- AS relative_share
   FROM  (
      SELECT t.txt, count(*) AS cnt
      FROM   token t
      WHERE  t.chartype = 'ALPHABETIC'
      GROUP  BY t.txt
      ORDER  BY cnt DESC
      LIMIT  _max_tokens
      ) t
   ORDER  BY t.cnt DESC;
END
$func$;

A expressão sum(t.cnt) OVER () é uma função de janela. Você poderia use um CTE em vez da subconsulta. Bonito, mas uma subconsulta geralmente é mais barata em casos simples como este (principalmente antes do Postgres 12).

Um RETURN explícito final declaração é não obrigatório (mas permitido) ao trabalhar com OUT parâmetros ou RETURNS TABLE (que faz uso implícito de OUT parâmetros).

round() com dois parâmetros só funciona para numeric tipos. count() na subconsulta produz um bigint resultado e uma sum() sobre este bigint produz um numeric resultado, assim lidamos com um numeric número automaticamente e tudo se encaixa.