Използвайте 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$;
Обадете се:
SELECT * FROM word_frequency(123);
Изричното дефиниране на типа на връщане е много по-практично от връщането на общ record
. По този начин не е нужно да предоставяте списък с дефиниции на колони с всяко извикване на функция. RETURNS TABLE
е един от начините да направите това. Има и други. Типове данни OUT
параметрите трябва да съвпадат точно с това, което се връща от заявката.
Изберете имена за OUT
параметри внимателно. Те са видими в тялото на функцията почти навсякъде. Квалифицирайте колони със същото име, за да избегнете конфликти или неочаквани резултати. Направих това за всички колони в моя пример.
Но обърнете внимание на потенциалния конфликт при именуване между OUT
параметър cnt
и псевдоним на колона със същото име. В този конкретен случай (RETURN QUERY SELECT ...
) Postgres използва псевдонима на колоната над OUT
параметър така или иначе. Това обаче може да бъде двусмислено в други контексти. Има различни начини да избегнете объркване:
- Използвайте подредната позиция на елемента в списъка SELECT:
ORDER BY 2 DESC
. Пример:- Изберете ли първия ред във всяка група GROUP BY?
- Повторете израза
ORDER BY count(*)
. - (Не е приложимо тук.) Задайте конфигурационния параметър
plpgsql.variable_conflict
или използвайте специалната команда#variable_conflict error | use_variable | use_column
във функцията. Вижте:- Конфликт на именуване между параметър на функцията и резултат от JOIN с клауза USING
Не използвайте "текст" или "брой" като имена на колони. И двете са законни за използване в Postgres, но "брой" е запазена дума в стандартен SQL и име на основна функция и "текст" е основен тип данни. Може да доведе до объркващи грешки. Използвам txt
и cnt
в моите примери може да искате по-ясни имена.
Добавен липсващ ;
и коригира синтактична грешка в заглавката. (_max_tokens int)
, а не (int maxTokens)
- тип след име .
Докато работите с целочислено деление, е по-добре първо да умножите и да разделите по-късно, за да сведете до минимум грешката при закръгляване. Или работете с numeric
или тип с плаваща запетая. Вижте по-долу.
Алтернатива
Това мисля вашата заявка всъщност трябва да изглежда така (изчисляване на относителен дял на токен ):
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$;
Изразът sum(t.cnt) OVER ()
е функция на прозореца. Вие можете използвайте CTE вместо подзаявката. Хубаво, но подзаявката обикновено е по-евтина в прости случаи като този (предимно преди Postgres 12).
Последно изрично RETURN
изявлението е не задължително (но разрешено) при работа с OUT
параметри или RETURNS TABLE
(което имплицитно използва OUT
параметри).
round()
с два параметъра работи само за numeric
видове. count()
в подзаявката произвежда bigint
резултат и sum()
над този bigint
произвежда numeric
резултат, така че имаме работа с numeric
номер автоматично и всичко просто си идва на мястото.