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) 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$ LANGUAGE plpgsql;
Ligar:
SELECT * FROM word_frequency(123);
Explicação:
É muito mais prático definir explicitamente o tipo de retorno do que simplesmente declará-lo como registro. 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. Os tipos de dados dos OUTparâmetros devem corresponder exatamente ao que é retornado pela consulta.
Escolha os nomes dos OUTparâmetros com cuidado. Eles são visíveis no corpo da função em quase qualquer lugar. Colunas de qualificação de tabela com o mesmo nome para evitar conflitos ou resultados inesperados. Fiz isso para todas as colunas do meu exemplo.
Mas observe o possível conflito de nomenclatura entre o OUTparâmetro cnte o alias da coluna de mesmo nome. Neste caso particular ( RETURN QUERY SELECT ...) Postgres usa o alias da coluna sobre o OUTparâmetro de qualquer maneira. Isso pode ser ambíguo em outros contextos, no entanto. Existem várias maneiras de evitar qualquer confusão:
- Use a posição ordinal do item na lista SELECT:
ORDER BY 2 DESC. Exemplo:
- Repita a expressão
ORDER BY count(*).
- (Não aplicável aqui.) Defina o parâmetro de configuração
plpgsql.variable_conflictou use o comando especial #variable_conflict error | use_variable | use_columnna função. Vejo:
Não use "texto" ou "contagem" como nomes de coluna. Ambos são permitidos no Postgres, mas "contagem" é uma palavra reservada no SQL padrão e um nome de função básica e "texto" é um tipo de dados básico. Pode levar a erros confusos. Eu uso txte cntem meus exemplos.
Foi adicionado um ;erro de sintaxe ausente e corrigido no cabeçalho. (_max_tokens int), não (int maxTokens)- digite após o nome .
Ao trabalhar com divisão inteira, é melhor multiplicar primeiro e dividir depois, para minimizar o erro de arredondamento. Ainda melhor: trabalhe com numeric(ou um tipo de ponto flutuante). Ver abaixo.
Alternativo
É assim que eu acho que sua consulta realmente deve ser (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) 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$ LANGUAGE plpgsql;
A expressão sum(t.cnt) OVER ()é uma função de janela . Você poderia usar um CTE em vez da subconsulta - bonito, mas uma subconsulta é normalmente mais barata em casos simples como este.
Uma declaração explícitaRETURN final não é necessária (mas permitida) ao trabalhar com OUTparâmetros ou RETURNS TABLE(o que faz uso implícito de OUTparâmetros).
round()com dois parâmetros só funciona para numerictipos. count()na subconsulta produz um bigintresultado e um sum()over this bigintproduz um numericresultado, portanto, lidamos com um numericnúmero automaticamente e tudo simplesmente se encaixa.