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_conflict
eller brug den specielle kommando#variable_conflict error | use_variable | use_column
i 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.