Использование 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;
Вызов:
SELECT * FROM word_frequency(123);
Объяснение:
намного более практично явно определить тип возвращаемого значения, чем просто объявить его как запись.Таким образом, вам не нужно предоставлять список определений столбцов при каждом вызове функции.RETURNS TABLE
- это один из способов сделать это.Есть и другие.Типы данных OUT
параметров должны точно соответствовать тому, что возвращается запросом.
Тщательно выбирайте имена для OUT
параметров.Они видны в теле функции практически везде.Уточняйте столбцы с одинаковыми именами, чтобы избежать конфликтов или непредвиденных результатов.Я сделал это для всех столбцов в моем примере.
Но обратите внимание на потенциальный конфликт имен между параметром OUT
cnt
и псевдонимом столбца с тем же именем.В этом конкретном случае (RETURN QUERY SELECT ...
) Postgres в любом случае использует псевдоним столбца над параметром OUT
.Это может быть неоднозначным в других контекстах, однако.Существуют различные способы избежать путаницы:
- Используйте порядковый номер элемента в списке SELECT:
ORDER BY 2 DESC
.Пример: - Повторить выражение
ORDER BY count(*)
. - (Неприменимо здесь.) Установите параметр конфигурации
plpgsql.variable_conflict
или используйте специальную команду #variable_conflict error | use_variable | use_column
в функции.См .:
Неиспользуйте «текст» или «количество» в качестве имен столбцов.И то, и другое разрешено использовать в Postgres, но «count» - это зарезервированное слово в стандартном SQL, а базовое имя функции и «text» - базовый тип данных.Может привести к запутанным ошибкам.Я использую 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) 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;
Выражение sum(t.cnt) OVER ()
представляет собой оконную функцию .Вы могли бы использовать CTE вместо подзапроса - довольно, но подзапрос, как правило, дешевле в простых случаях, таких как этот.
Окончательный явный RETURN
оператор не требуется (но разрешен) при работе с OUT
параметрами или RETURNS TABLE
(что подразумевает использование OUT
параметров).
round()
с двумя параметрами работает только для numeric
типов.count()
в подзапросе дает результат bigint
, а sum()
над этим bigint
дает результат numeric
, таким образом, мы автоматически имеем дело с numeric
числом, и все просто становится на свои места.