Використання 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
параметром у будь-якому випадку. Однак це може бути неоднозначним і в інших контекстах. Існують різні способи уникнути будь-якої плутанини:
- Використовуйте порядковий номер елемента в списку вибору:
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
автоматично маємо справу з числом, і все просто стає на свої місця.