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

ajsxfq5m  于 2022-11-29  发布在  PostgreSQL
关注(0)|答案(3)|浏览(838)

就我所知,我的函数与我所看到的示例非常相似。有人能告诉我如何让它工作吗?

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;
fcg9iug3

fcg9iug31#

返回所选列

CREATE OR REPLACE FUNCTION get_user_by_username(_username text
                                              , _online bool DEFAULT false)
  RETURNS TABLE (
    user_id int
  , user_name varchar
  , last_activity timestamptz
  )
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp  -- ts with time zone
      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$;

电话:

SELECT * FROM get_user_by_username('myuser', true);

你有DECLARE result record;,但没有使用变量。我删除了cruft。
您可以直接从UPDATE传回记录,这比呼叫额外的SELECT陈述式快得多。请使用RETURN QUERYUPDATE with a RETURNING clause
如果用户不是_online,则默认为普通的SELECT。如果省略第二个参数,这也是(安全)默认值-只有在函数定义中为DEFAULT false提供该默认值后,才可能省略第二个参数。
如果在函数内部的查询中不对列名进行表限定(tablename.columnname),请注意列名和命名参数之间的命名冲突,这在函数内部的任何地方都是可见的(大多数情况下)。
您也可以使用参数的位置参照($n)来避免这种恩怨。或者使用您 * 从不 * 用于数据行名称的前置词:例如下划线(_username)。
如果**users.username在表中定义为unique**,则第二个查询中的LIMIT 1只是cruft。如果它不是,则UPDATE可以更新多行,这很可能是错误的。我假设username是唯一的,并修剪噪声。
定义函数返回类型(如@ertx所示),或者必须为每个函数调用提供一个列定义列表,这是很尴尬的。
为此目的创建一个类型(像@ertx建议的那样)是一种有效的方法,但对于单个函数来说可能有些过头了。这是在我们有RETURNS TABLE之前旧版本Postgres的做法--就像上面演示的那样。
对于这个简单的函数,您不需要循环
每个函数都需要一个语言声明。在本例中为**LANGUAGE plpgsql**。
我使用timestamptztimestamp with time zone)而不是timestamptimestamp without time zone),这是一个正常的默认值。

  • 在Rails和PostgreSQL中完全忽略时区

返回整行(一组)

要返回现有表users的 * 所有列 *,有一个更简单的方法。Postgres自动为每个表定义一个同名的复合类型。只需使用RETURNS SETOF users就可以大大简化查询:

CREATE OR REPLACE FUNCTION get_user_by_username(_username text
                                              , _online bool DEFAULT false)
  RETURNS SETOF users
  LANGUAGE plpgsql 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$;

返回整行加上自定义加法

为了解决TheRealChx 101在下面的评论中添加的问题:
如果除了整个表格之外,您还有一个计算值,该怎么办?😑
虽然不是那么简单,但也是可行的。我们可以将整个行类型作为 one 字段发送,然后添加更多:

CREATE OR REPLACE FUNCTION get_user_by_username3(_username text
                                               , _online bool DEFAULT false)
  RETURNS TABLE (
    users_row users
  , custom_addition text
  )
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp  -- ts with time zone
      WHERE  u.user_name = _username
      RETURNING u  -- whole row
              , u.user_name || u.user_id;
   ELSE
      RETURN QUERY
      SELECT u, u.user_name || u.user_id
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

“魔术”就在函数调用中,我们(可选地)分解行类型:

SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
  • db〈〉fiddle here *(显示全部)

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

  • 重构PL/pgSQL函数以返回各种SELECT查询的输出
e3bfsja2

e3bfsja22#

如果希望创建返回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)

(you可能需要更改数据类型)
我个人更喜欢类型方法。它确保了如果函数被编辑,所有的查询都将返回正确的结果。这可能是一个痛苦,因为每次你修改函数的参数,你都需要重新创建/删除类型。
例如:

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;
dtcbnfnu

dtcbnfnu3#

由于只返回一条记录(通过LIMIT 1),因此也可以删除SETOF并声明如下函数:

create or replace function x() returns record as $$
declare result record;
begin
  select
    1, 2, 3
  into result;
  return result;
end;
$$ language plpgsql;

然后,从SELECT子句而不是FROM子句引用该函数(如果您没有使用类型化记录,则仍然不可能这样做):

select x();

结果如下所示:

|x      |
|-------|
|(1,2,3)|

相关问题