Как вернуть результат SELECT внутри функции в PostgreSQL? - программирование
Подтвердить что ты не робот

Как вернуть результат SELECT внутри функции в PostgreSQL?

У меня есть эта функция в PostgreSQL, но я не знаю, как вернуть результат запроса:

CREATE OR REPLACE FUNCTION wordFrequency(maxTokens INTEGER)
  RETURNS SETOF RECORD AS
$$
BEGIN
    SELECT text, count(*), 100 / maxTokens * count(*)
    FROM (
        SELECT text
    FROM token
    WHERE chartype = 'ALPHABETIC'
    LIMIT maxTokens
    ) as tokens
    GROUP BY text
    ORDER BY count DESC
END
$$
LANGUAGE plpgsql;

Но я не знаю, как вернуть результат запроса внутри функции PostgreSQL.

Я обнаружил, что тип возврата должен быть SETOF RECORD, правильно? Но команда return неверна.

Каков правильный способ сделать это?

4b9b3361

Ответ 1

Используйте 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. Это может быть неоднозначным в других контекстах, однако. Существуют различные способы избежать путаницы:

    1. Используйте порядковый номер элемента в списке SELECT: ORDER BY 2 DESC. Пример:
    2. Повторите выражение ORDER BY count(*).
    3. (Не применимо здесь.) Установите параметр конфигурации plpgsql.variable_conflict или используйте специальную команду #variable_conflict error | use_variable | use_column #variable_conflict error | use_variable | use_column #variable_conflict error | use_variable | use_column в функции. Увидеть:
  • Не используйте "текст" или "считать" в качестве имен столбцов. И то, и другое разрешено использовать в Postgres, но "count" - зарезервированное слово в стандартном SQL, а базовое имя функции и "text" - базовый тип данных. Может привести к запутанным ошибкам. Я использую txt и cnt в моих примерах.

  • Добавлен недостающий ; и исправил синтаксическую ошибку в заголовке. (_max_tokens int), not (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 числом, и все становится на свои места.

Ответ 2

Привет, пожалуйста, проверьте ссылку ниже

https://www.postgresql.org/docs/current/xfunc-sql.html

EX:

CREATE FUNCTION sum_n_product_with_tab (x int)
RETURNS TABLE(sum int, product int) AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;