Använd 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:
SELECT * FROM word_frequency(123);
Att uttryckligen definiera returtypen är mycket mer praktiskt än att returnera en generisk record
. På så sätt behöver du inte tillhandahålla en kolumndefinitionslista med varje funktionsanrop. RETURNS TABLE
är ett sätt att göra det. Det finns andra. Datatyper för OUT
parametrar måste matcha exakt vad som returneras av frågan.
Välj namn för OUT
parametrar noggrant. De är synliga i funktionskroppen nästan var som helst. Tabellkvalificera kolumner med samma namn för att undvika konflikter eller oväntade resultat. Jag gjorde det för alla kolumner i mitt exempel.
Men notera den potentiella namnkonflikten mellan OUT
parameter cnt
och kolumnaliaset med samma namn. I det här specifika fallet (RETURN QUERY SELECT ...
) Postgres använder kolumnaliaset över OUT
parameter åt båda hållen. Detta kan dock vara tvetydigt i andra sammanhang. Det finns olika sätt att undvika förvirring:
- Använd objektets ordningsposition i SELECT-listan:
ORDER BY 2 DESC
. Exempel:- Välj första raden i varje GROUP BY-grupp?
- Upprepa uttrycket
ORDER BY count(*)
. - (Inte tillämpligt här.) Ställ in konfigurationsparametern
plpgsql.variable_conflict
eller använd specialkommandot#variable_conflict error | use_variable | use_column
i funktionen. Se:- Namnkonflikt mellan funktionsparameter och resultat av JOIN med USING-sats
Använd inte "text" eller "count" som kolumnnamn. Båda är lagliga att använda i Postgres, men "räkna" är ett reserverat ord i standard SQL och ett grundläggande funktionsnamn och "text" är en grundläggande datatyp. Kan leda till förvirrande fel. Jag använder txt
och cnt
i mina exempel kanske du vill ha mer explicita namn.
Lade till en saknad ;
och korrigerade ett syntaxfel i rubriken. (_max_tokens int)
, inte (int maxTokens)
- typ efter namn .
När du arbetar med heltalsdivision är det bättre att multiplicera först och dividera senare för att minimera avrundningsfelet. Eller arbeta med numeric
eller en flyttalstyp. Se nedan.
Alternativ
Detta är vad jag tror din fråga borde faktiskt se ut (beräknar en relativ andel per 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$;
Uttrycket sum(t.cnt) OVER ()
är en fönsterfunktion. Du kunde använd en CTE istället för underfrågan. Snyggt, men en underfråga är vanligtvis billigare i enkla fall som det här (mest före Postgres 12).
En sista uttrycklig RETURN
uttalandet är inte krävs (men tillåtet) när du arbetar med OUT
parametrar eller RETURNS TABLE
(som gör implicit användning av OUT
parametrar).
round()
med två parametrar fungerar bara för numeric
typer. count()
i underfrågan producerar en bigint
resultat och en sum()
över denna bigint
producerar en numeric
resultat, därför hanterar vi en numeric
nummer automatiskt och allt faller bara på plats.