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:- Use a posição ordinal do item na lista SELECT:
ORDER BY 2 DESC
. Exemplo:- Selecionar a primeira linha em cada grupo GROUP BY?
- Repita a expressão
ORDER BY count(*)
. - (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.