選択した列を返す
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;
しかし、変数を使用しませんでした。残骸を削除しました。
UPDATE
から直接レコードを返すことができます 、追加のSELECT
を呼び出すよりもはるかに高速です 声明。 RETURN QUERY
を使用する およびUPDATE
RETURNING
を使用 条項。
ユーザーが_online
でない場合 、デフォルトはプレーンなSELECT
。これは、2番目のパラメーターが省略されている場合の(安全な)デフォルトでもあります。これは、デフォルトにDEFAULT false
を指定した後にのみ可能です。 関数定義で。
列名をテーブル修飾しない場合(tablename.columnname
)関数内のクエリでは、名前の競合に注意してください 関数内のどこにでも(ほとんど)表示される列名と名前付きパラメーターの間。
位置参照($n
を使用することで、このような競合を回避することもできます。 )パラメータ用。または、決して使用しないプレフィックスを使用します 列名に使用:アンダースコア(_username
など) 。
users.username
の場合 一意と定義されています テーブルで、LIMIT 1
2番目のクエリではただのくだらないです。 ない場合 、次にUPDATE
複数の行を更新できますが、これはおそらく間違っている 。一意のusername
を想定しています ノイズをトリミングします。
関数の戻り値タイプを定義します (@ertxが示すように)または、すべての関数呼び出しで列定義リストを提供する必要がありますが、これは厄介です。
その目的のために型を作成すること(@ertxが提案するような)は有効なアプローチですが、おそらく単一の関数にはやり過ぎです。これが、 RETURNS TABLE
が登場する前の古いバージョンのPostgresに移行する方法でした。 その目的のために-上記のように。
ループは必要ありません この単純な関数のために。
すべての関数には言語宣言が必要です。 LANGUAGE plpgsql
この場合。
timestamptz
を使用しています (timestamp with time zone
)timestamp
の代わりに (timestamp 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$;
行全体とカスタム追加を返す
以下のコメントでTheRealChx101によって追加された質問に対処するには:
テーブル全体に加えて計算値もある場合はどうなりますか? 😑
それほど単純ではありませんが、実行可能です。行タイプ全体を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<>ここでフィドル (すべて表示)
より「動的」なものが必要な場合は、次のことを検討してください。
- PL / pgSQL関数をリファクタリングして、さまざまなSELECTクエリの出力を返します