Zastosowanie 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) 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$ LANGUAGE plpgsql;
Połączenie:
SELECT * FROM word_frequency(123);
Wyjaśnienie:
O wiele bardziej praktyczne jest jawne zdefiniowanie typu zwracanego niż zwykłe zadeklarowanie go jako rekordu. W ten sposób nie musisz podawać listy definicji kolumn przy każdym wywołaniu funkcji. RETURNS TABLE
jest jednym ze sposobów, aby to zrobić. Są inni. Typy danych OUT
parametrów muszą dokładnie odpowiadać temu, co jest zwracane przez zapytanie.
OUT
Uważnie wybieraj nazwy parametrów. Są widoczne w korpusie funkcji prawie wszędzie. Kwalifikuj kolumny o tej samej nazwie, aby uniknąć konfliktów lub nieoczekiwanych wyników. Zrobiłem to dla wszystkich kolumn w moim przykładzie.
Zwróć jednak uwagę na potencjalny konflikt nazw między OUT
parametrem cnt
a aliasem kolumny o tej samej nazwie. W tym konkretnym przypadku ( RETURN QUERY SELECT ...
) Postgres używa aliasu kolumny zamiast OUT
parametru w obie strony. Może to być jednak niejednoznaczne w innych kontekstach. Istnieje wiele sposobów uniknięcia nieporozumień:
- Użyj porządkowej pozycji pozycji na liście SELECT:
ORDER BY 2 DESC
. Przykład:
- 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. Widzieć:
Nie używaj „tekst” ani „liczba” jako nazw kolumn. Oba są legalne w Postgresie, ale „count” jest słowem zastrzeżonym w standardowym SQL, a nazwa funkcji i „tekst” to podstawowy typ danych. Może prowadzić do mylących błędów. Używam txt
i cnt
w moich przykładach.
Dodano brakujący ;
i poprawiono błąd składni w nagłówku. (_max_tokens int)
, nie (int maxTokens)
- wpisz po nazwie .
Podczas pracy z dzieleniem całkowitoliczbowym lepiej jest najpierw pomnożyć, a później podzielić, aby zminimalizować błąd zaokrąglania. Jeszcze lepiej: pracuj z numeric
(lub typem zmiennoprzecinkowym). Zobacz poniżej.
Alternatywny
To jest to, co ja myślę zapytanie powinno wyglądać w rzeczywistości (obliczenie względnego udziału za żeton ):
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text
, abs_cnt bigint
, relative_share numeric) 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$ LANGUAGE plpgsql;
Wyrażenie sum(t.cnt) OVER ()
jest funkcją okna . Ty mógł użyć CTE zamiast podkwerendzie - ładna, ale podzapytanie jest zazwyczaj tańsze w prostych przypadkach takich jak ten.
Ostateczna jawna RETURN
instrukcja nie jest wymagana (ale dozwolona) podczas pracy z OUT
parametrami lub RETURNS TABLE
(co powoduje niejawne użycie OUT
parametrów).
round()
z dwoma parametrami działa tylko dla numeric
typów. count()
w podzapytaniu daje bigint
wynik, a sum()
over to bigint
daje numeric
wynik, więc mamy do czynienia z numeric
liczbą automatycznie i wszystko po prostu układa się na swoim miejscu.