web-dev-qa-db-ja.com

PostgreSQL:エラー:42601:「レコード」を返す関数には列定義リストが必要です

(免責事項:PostgreSQL初心者。)

OK、私が知る限り、私の機能は私が見たサンプルに適切に似ています。誰かが私がこれをどのように機能させるかについて私に手掛かりを与えることができますか?

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;
35
Jeremy Holovacs

setofレコードを返す関数を作成する場合は、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;
29
ertx

選択した列を返す

_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_)を使用することにより、このような競合を回避することもできます。または、neverアンダースコア(__username_)のように列名に使用するプレフィックスを使用します。

  • _users.username_がテーブルでniqueに定義されている場合、2番目のクエリの_LIMIT 1_はただの問題です。
    notの場合、UPDATEは複数の行を更新できます。これは、おそらく間違ったです。
    一意のusernameを想定して、クラフを削除しました。

  • 関数の戻り値のタイプを定義します(@ertxの例のように)。そうしないと、すべての関数呼び出しで列定義リストを提供する必要があります。

  • (@ertxが提案するような)その目的のための型を作成することは有効なアプローチですが、おそらく単一の機能ではやり過ぎです。 _RETURNS TABLE_ になる前に、それは古いバージョンのPostgreSQLに移行する方法でした-上記のように。

  • この単純な関数では、ループは必要ありません

  • すべての関数には言語宣言が必要です。 _LANGUAGE plpgsql_この場合。

  • パラメータの長さ制限(varchar(250))を定義しても意味がありません。 textと入力するように簡略化しました。

テーブル全体を返す

テーブルusersの-​​all columnsを返したい場合は、もっと簡単な方法があります。 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;
_

もっと「動的」なものが必要な場合は、以下を検討してください。

41