Використання 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автоматично маємо справу з числом, і все просто стає на свої місця.