Utilisez 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$;
Appel :
SELECT * FROM word_frequency(123);
Définir explicitement le type de retour est beaucoup plus pratique que de renvoyer un record
générique . De cette façon, vous n'avez pas à fournir une liste de définition de colonne avec chaque appel de fonction. RETURNS TABLE
est une façon de le faire. Il y en a d'autres. Types de données de OUT
les paramètres doivent correspondre exactement à ce qui est renvoyé par la requête.
Choisissez des noms pour OUT
paramètres avec soin. Ils sont visibles dans le corps de la fonction presque n'importe où. Qualifiez les colonnes de table du même nom pour éviter les conflits ou les résultats inattendus. Je l'ai fait pour toutes les colonnes de mon exemple.
Mais notez le potentiel conflit de nom entre le OUT
paramètre cnt
et l'alias de colonne du même nom. Dans ce cas particulier (RETURN QUERY SELECT ...
) Postgres utilise l'alias de colonne sur le OUT
paramètre de toute façon. Cela peut cependant être ambigu dans d'autres contextes. Il existe plusieurs manières d'éviter toute confusion :
- Utilisez la position ordinale de l'élément dans la liste SELECT :
ORDER BY 2 DESC
. Exemple :- Sélectionner la première ligne de chaque groupe GROUP BY ?
- Répéter l'expression
ORDER BY count(*)
. - (Non applicable ici.) Définissez le paramètre de configuration
plpgsql.variable_conflict
ou utilisez la commande spéciale#variable_conflict error | use_variable | use_column
dans la fonction. Voir :- Conflit de nom entre le paramètre de la fonction et le résultat de JOIN avec la clause USING
N'utilisez pas "text" ou "count" comme noms de colonnes. Les deux sont légaux à utiliser dans Postgres, mais "count" est un mot réservé en SQL standard et un nom de fonction de base et "texte" est un type de données de base. Peut conduire à des erreurs confuses. J'utilise txt
et cnt
dans mes exemples, vous voudrez peut-être des noms plus explicites.
Ajout d'un ;
manquant et corrigé une erreur de syntaxe dans l'en-tête. (_max_tokens int)
, pas (int maxTokens)
- tapez après nom .
Lorsque vous travaillez avec une division entière, il est préférable de multiplier d'abord et de diviser ensuite, afin de minimiser l'erreur d'arrondi. Ou travaillez avec numeric
ou un type à virgule flottante. Voir ci-dessous.
Alternative
C'est ce que je pense votre requête devrait ressembler à (calcul d'une part relative par jeton ):
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$;
L'expression sum(t.cnt) OVER ()
est une fonction de fenêtre. Vous pourriez utiliser un CTE au lieu de la sous-requête. Jolie, mais une sous-requête est généralement moins chère dans des cas simples comme celui-ci (principalement avant Postgres 12).
Un dernier RETURN
explicite l'instruction n'est pas requis (mais autorisé) lorsque vous travaillez avec OUT
paramètres ou RETURNS TABLE
(qui utilise implicitement OUT
paramètres).
round()
avec deux paramètres ne fonctionne que pour numeric
les types. count()
dans la sous-requête produit un bigint
résultat et un sum()
sur ce bigint
produit un numeric
résultat, on a donc affaire à un numeric
numéro automatiquement et tout se met en place.