Użyj 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$;
Zadzwoń:
SELECT * FROM word_frequency(123);
Jawne zdefiniowanie typu zwracanego to dużo bardziej praktyczne niż zwracanie ogólnego record
. W ten sposób nie musisz dostarczać listy definicji kolumn przy każdym wywołaniu funkcji. RETURNS TABLE
jest jednym ze sposobów na zrobienie tego. Są inni. Typy danych OUT
parametry muszą dokładnie odpowiadać temu, co jest zwracane przez zapytanie.
Wybierz nazwy dla OUT
parametry ostrożnie. Są one widoczne w treści funkcji niemal wszędzie. Kolumny kwalifikujące do tabeli o tej samej nazwie, aby uniknąć konfliktów lub nieoczekiwanych wyników. Zrobiłem to dla wszystkich kolumn w moim przykładzie.
Ale zwróć uwagę na potencjalny konflikt nazw między OUT
parametr cnt
i alias kolumny o tej samej nazwie. W tym konkretnym przypadku (RETURN QUERY SELECT ...
) Postgres używa aliasu kolumny zamiast OUT
parametr w obie strony. Jednak w innych kontekstach może to być niejednoznaczne. Istnieje wiele sposobów na uniknięcie nieporozumień:
- Użyj pozycji porządkowej elementu na liście SELECT:
ORDER BY 2 DESC
. Przykład:- Wybrać pierwszy wiersz w każdej grupie GROUP BY?
- Powtórz wyrażenie
ORDER BY count(*)
. - (Nie dotyczy tutaj.) Ustaw parametr konfiguracyjny
plpgsql.variable_conflict
lub użyj specjalnego polecenia#variable_conflict error | use_variable | use_column
w funkcji. Zobacz:- Konflikt nazw między parametrem funkcji a wynikiem JOIN z klauzulą USING
Nie używaj „tekstu” ani „liczba” jako nazw kolumn. Oba są legalne w Postgresie, ale „liczba” jest słowem zastrzeżonym w standardowym SQL i nazwa funkcji podstawowej i "tekst" to podstawowy typ danych. Może prowadzić do mylących błędów. Używam txt
i cnt
w moich przykładach możesz potrzebować bardziej wyraźnych nazw.
Dodano brakujący ;
i poprawiono błąd składni w nagłówku. (_max_tokens int)
, a nie (int maxTokens)
- wpisz po nazwie .
Pracując z dzieleniem liczb całkowitych, lepiej najpierw pomnożyć, a później podzielić, aby zminimalizować błąd zaokrąglania. Lub pracuj z numeric
lub typu zmiennoprzecinkowego. Zobacz poniżej.
Alternatywna
Tak właśnie myślę Twoje zapytanie powinno faktycznie wyglądać (obliczanie względnego udziału 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$;
Wyrażenie sum(t.cnt) OVER ()
to funkcja okna. możesz użyj CTE zamiast podzapytania. Ładne, ale podzapytanie jest zazwyczaj tańsze w prostych przypadkach, takich jak ten (głównie przed Postgresem 12).
Ostateczny wyraźny RETURN
oświadczenie nie wymagane (ale dozwolone) podczas pracy z OUT
parametry lub RETURNS TABLE
(co powoduje niejawne użycie OUT
parametry).
round()
z dwoma parametrami działa tylko dla numeric
typy. count()
w podzapytaniu tworzy bigint
wynik i sum()
nad tym bigint
tworzy numeric
wynik, więc mamy do czynienia z numeric
numer automatycznie i wszystko po prostu ułoży się na swoim miejscu.