PostgreSQL: ОШИБКА: 42601: список функций необходим для функций, возвращающих «запись» - PullRequest
35 голосов
/ 22 декабря 2011

(Отказ от ответственности: новичок в PostgreSQL.)

Хорошо, насколько я могу судить, моя функция правильно напоминает примеры, которые я видел.Может кто-нибудь подсказать мне, как мне заставить это работать?

create or replace function get_user_by_username(
    username varchar(250),
    online boolean
    ) returns setof record as $$
declare result record;
begin

    if online then 
        update users
        set last_activity = current_timestamp
        where user_name = username;
    end if;

    return query
    select
        user_id,
        user_name,
        last_activity,
        created,
        email,
        approved,
        last_lockout,
        last_login,
        last_password_changed,
        password_question,
        comment
    from
        users
    where
        user_name = username
    limit 1;

    return;
end;
$$ language plpgsql;

Ответы [ 2 ]

41 голосов
/ 23 декабря 2011

Возврат выбранных столбцов

CREATE OR REPLACE FUNCTION get_user_by_username(_username text, _online bool)
  RETURNS TABLE (
    user_id int
   ,user_name text
   ,last_activity timestamp
   , ... ) AS
$func$
BEGIN

IF _online THEN
   RETURN QUERY
   UPDATE users u 
   SET    last_activity = current_timestamp
   WHERE  u.user_name = _username
   RETURNING
          u.user_id
         ,u.user_name
         ,u.last_activity
         , ... ;
ELSE
   RETURN QUERY
   SELECT u.user_id
         ,u.user_name
         ,u.last_activity
         , ...
   FROM   users u
   WHERE  u.user_name = _username;
END IF;

END
$func$  LANGUAGE plpgsql;

Вызов:

SELECT * FROM get_user_by_username('myuser', TRUE)

Основные точки

  • У вас было DECLARE result record;, но вы не использовалипеременная.Я удалил кражу.

  • Вы можете вернуть запись непосредственно из UPDATE, что намного быстрее, чем вызов дополнительного оператора SELECT.Используйте RETURN QUERY и UPDATE с предложением RETURNING .
    Если пользователь не _online, по умолчанию используется обычное значение SELECT.

  • Если вы не указали имена столбцов в таблице (tablename.columnname) в запросах внутри функции, опасайтесь конфликтов имен между именами столбцов и именованными параметрами, которые видны (чаще всего) везде внутрифункция.
    Вы также можете избежать таких конфликтов, используя позиционные ссылки ($n) для параметров.Или используйте префикс, который вы никогда не используете для имен столбцов: например, подчеркивание (_username).

  • Если users.usernameопределяется уникальным в вашей таблице, тогда LIMIT 1 во втором запросе просто бессмысленно.
    Если это не , тогда UPDATE может обновить несколько строк, чтоСкорее всего неправильно .
    Я предположил уникальный username и удалил кражу.

  • Определите тип возврата функции (как продемонстрировано @ertx) или вам придется предоставлять список определений столбцов при каждом вызове функции, что неудобно.

  • Создание типа для этой цели (как предложено @ertx)правильный подход, но, вероятно, излишний для одной функции.Это был путь в старых версиях PostgreSQL до того, как для этого у нас было RETURNS TABLE - как показано выше.

  • You для этой простой функции не требуется цикл .

  • Для каждой функции требуется объявление языка.LANGUAGE plpgsql в этом случае.

  • Вероятно, нет смысла определять ограничение длины (varchar(250)) для параметра.Я упростил до типа text.

Вернуть всю таблицу

Если вы хотите вернуть все столбцы таблицыusers, есть более простой способ.PostgreSQL автоматически определяет составной тип с тем же именем для каждой таблицы .В этом случае вы можете использовать RETURNS SETOF users и значительно упростить запрос:

CREATE OR REPLACE FUNCTION get_user_by_username(_username text, _online bool)
  RETURNS SETOF users AS
$func$
BEGIN

IF _online THEN
    RETURN QUERY
    UPDATE users u 
    SET    last_activity = current_timestamp
    WHERE  u.user_name = _username
    RETURNING u.*;
ELSE
    RETURN QUERY
    SELECT *
    FROM   users u
    WHERE  u.user_name = _username;
END IF;

END
$func$  LANGUAGE plpgsql;

Если вам нужно что-то более «динамическое», рассмотрите:

29 голосов
/ 22 декабря 2011

если вы хотите создать функцию, возвращающую setof record, вам нужно определить типы столбцов в вашем операторе select

Дополнительная информация

Ваш запрос должен выглядетьчто-то вроде этого:

select * from get_user_by_username('Username', True) as 
  f(user_id integer, user_name varchar, last_activity, varchar, created date, email        archar, approved boolean, last_lockout timestamp, last_login timestamp, 
  last_password_changed timestamp, password_question varchar, comment varchar)

(вам, вероятно, потребуется изменить типы данных)

Лично я предпочитаю подход с типами.это гарантирует, что если функция отредактирована, все запросы будут возвращать правильные результаты.Это может быть неприятно, потому что каждый раз, когда вы изменяете аргументы функции, вам нужно заново создавать / отбрасывать типы, а также.

Например:

CREATE TYPE return_type as 
(user_id integer,
 user_name varchar,
 last_activity varchar,
 created timestamp,
 email varchar,
 approved boolean,
 last_lockout timestamp ,
 last_login timestamp,
 last_password_changed timestamp,
 password_question varchar,
 comment varchar);

create or replace function get_user_by_username( username varchar(250), online 

boolean) returns setof return_type as $$
declare _rec return_type;
begin
    if online then 
        update users
        set last_activity = current_timestamp
        where user_name = username;
    end if;
    for _rec in select
        user_id,
        user_name,
        last_activity,
        created,
        email,
        approved,
        last_lockout,
        last_login,
        last_password_changed,
        password_question,
        comment
      from
        users
      where
        user_name = username
      limit 1 
    loop

      return next _rec;

    end loop

end;
$$ language plpgsql;
...