Brug 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$;
Ring til:
SELECT * FROM word_frequency(123);
At definere returtypen eksplicit er meget mere praktisk end at returnere en generisk record . På denne måde behøver du ikke at levere en kolonnedefinitionsliste med hvert funktionskald. RETURNS TABLE er en måde at gøre det på. Der er andre. Datatyper af OUT parametre skal matche nøjagtigt, hvad der returneres af forespørgslen.
Vælg navne til OUT parametre omhyggeligt. De er synlige i funktionskroppen næsten overalt. Tabelkvalificerer kolonner af samme navn for at undgå konflikter eller uventede resultater. Det gjorde jeg for alle kolonner i mit eksempel.
Men bemærk den potentielle navnekonflikt mellem OUT parameter cnt og kolonnealiaset af samme navn. I dette særlige tilfælde (RETURN QUERY SELECT ... ) Postgres bruger kolonnealiaset over OUT parameter begge veje. Dette kan dog være tvetydigt i andre sammenhænge. Der er forskellige måder at undgå forvirring på:
- Brug ordenspositionen for elementet i SELECT-listen:
ORDER BY 2 DESC. Eksempel:- Vælg første række i hver GROUP BY-gruppe?
- Gentag udtrykket
ORDER BY count(*). - (Ikke relevant her.) Indstil konfigurationsparameteren
plpgsql.variable_conflicteller brug den specielle kommando#variable_conflict error | use_variable | use_columni funktionen. Se:- Navngivningskonflikt mellem funktionsparameter og resultat af JOIN med USING-sætning
Brug ikke "tekst" eller "tæller" som kolonnenavne. Begge er lovlige at bruge i Postgres, men "tælle" er et reserveret ord i standard SQL og et grundlæggende funktionsnavn og "tekst" er en grundlæggende datatype. Kan føre til forvirrende fejl. Jeg bruger txt og cnt i mine eksempler vil du måske have mere eksplicitte navne.
Tilføjet en manglende ; og rettede en syntaksfejl i overskriften. (_max_tokens int) , ikke (int maxTokens) - skriv efter navn .
Mens du arbejder med heltalsdivision, er det bedre at gange først og dividere senere for at minimere afrundingsfejlen. Eller arbejd med numeric eller en flydende kommatype. Se nedenfor.
Alternativ
Det er hvad jeg mener din forespørgsel skal faktisk se ud (beregner en relativ andel pr. 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$;
Udtrykket sum(t.cnt) OVER () er en vinduesfunktion. Du kunne brug en CTE i stedet for underforespørgslen. Smukt, men en underforespørgsel er typisk billigere i simple tilfælde som denne (for det meste før Postgres 12).
En sidste eksplicit RETURN erklæring er ikke påkrævet (men tilladt), når du arbejder med OUT parametre eller RETURNS TABLE (som gør implicit brug af OUT parametre).
round() med to parametre virker kun for numeric typer. count() i underforespørgslen producerer en bigint resultat og en sum() over denne bigint producerer en numeric resultat, derfor beskæftiger vi os med en numeric nummer automatisk, og alt falder bare på plads.