Použijte 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$;
Volejte:
SELECT * FROM word_frequency(123);
Explicitní definování návratového typu je hodně praktičtější než vrácení obecného record
. Tímto způsobem nemusíte při každém volání funkce poskytovat seznam definic sloupců. RETURNS TABLE
je jeden způsob, jak to udělat. Jsou i další. Datové typy OUT
parametry musí přesně odpovídat tomu, co je vráceno dotazem.
Vyberte názvy pro OUT
parametry pečlivě. Jsou viditelné v těle funkce téměř kdekoli. Sloupce se stejným názvem upravte podle tabulky, abyste předešli konfliktům nebo neočekávaným výsledkům. Udělal jsem to pro všechny sloupce v mém příkladu.
Všimněte si však potenciálního konfliktu názvů mezi OUT
parametr cnt
a alias sloupce se stejným názvem. V tomto konkrétním případě (RETURN QUERY SELECT ...
) Postgres používá alias sloupce přes OUT
parametr v obou směrech. To však může být v jiných kontextech nejednoznačné. Existují různé způsoby, jak se vyhnout nejasnostem:
- Použijte pořadovou pozici položky v seznamu SELECT:
ORDER BY 2 DESC
. Příklad:- Vybrat první řádek v každé skupině GROUP BY?
- Opakujte výraz
ORDER BY count(*)
. - (Zde nelze použít.) Nastavte konfigurační parametr
plpgsql.variable_conflict
nebo použijte speciální příkaz#variable_conflict error | use_variable | use_column
ve funkci. Viz:- Konflikt pojmenování mezi parametrem funkce a výsledkem JOIN s klauzulí USING
Jako názvy sloupců nepoužívejte „text“ nebo „počet“. Obojí je legální pro použití v Postgresu, ale „počet“ je rezervované slovo ve standardním SQL a základní název funkce a "text" je základní datový typ. Může vést k matoucím chybám. Používám txt
a cnt
v mých příkladech možná budete chtít explicitnější jména.
Přidán chybějící ;
a opravena syntaktická chyba v záhlaví. (_max_tokens int)
, nikoli (int maxTokens)
- typ za jméno .
Při práci s celočíselným dělením je lepší nejprve násobit a později dělit, aby se minimalizovala chyba zaokrouhlování. Nebo pracujte s numeric
nebo typu s plovoucí desetinnou čárkou. Viz níže.
Alternativa
Tohle si myslím váš dotaz by měl ve skutečnosti vypadat (výpočet relativního podílu na 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$;
Výraz sum(t.cnt) OVER ()
je funkce okna. Mohli byste místo poddotazu použijte CTE. Pěkné, ale dílčí dotaz je v jednoduchých případech, jako je tento, obvykle levnější (většinou před Postgres 12).
Poslední explicitní RETURN
prohlášení není vyžadováno (ale povoleno) při práci s OUT
parametry nebo RETURNS TABLE
(což implicitně využívá OUT
parametry).
round()
se dvěma parametry funguje pouze pro numeric
typy. count()
v poddotazu vytvoří bigint
výsledek a sum()
přes tento bigint
vytvoří numeric
výsledkem je tedy numeric
číslo automaticky a vše zapadne na své místo.