PostgreSQL:错误:42601:返回“记录”的函数需要列定义列表

时间:2011-12-22 14:19:57

标签: postgresql plpgsql

(免责声明: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 个答案:

答案 0 :(得分:41)

返回选定的列

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 QUERYUPDATE with a RETURNING clause 如果用户不是_online,则默认为普通SELECT

  • 如果您没有在函数内的查询中对列名称(tablename.columnname)进行表限定,请注意列名和命名参数之间的命名冲突在功能内的任何地方都可见(大多数) 您还可以通过对参数使用位置引用($n)来避免此类冲突。或者使用从不用于列名称的前缀:如下划线(_username)。

  • 如果 users.username 在您的表格中定义为唯一,那么第二个查询中的LIMIT 1就是残缺的。
    如果,则UPDATE可以更新多行,这很可能错误
    我假设了一个独特的username并删除了残缺。

  • 定义函数的返回类型(如@ertx演示)或者您必须在每个函数调用中提供列定义列表,这很尴尬。

    < / LI>
  • 为此目的创建一个类型(如@ertx提议)是一种有效的方法,但对于单个函数可能有点过分。在我们为此目的RETURNS TABLE之前,这是旧版PostgreSQL的方法 - 如上所示。

  • 不需要循环来完成这个简单的功能。

  • 每个函数都需要语言声明。 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;

如果您需要更“动态”的内容,请考虑:

答案 1 :(得分:29)

如果你想创建函数返回setof记录,你需要在select语句中定义列类型

More info

您的查询应如下所示:

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;