2016-08-23 6 views
2

selectクエリを実行し、行が更新されている場合はその結果を返してくれるplpgsqlプロシージャを作成したいと思います。plpgsqlはそれを返す前にクエリを処理します

私は、SELECT nickname FROM use_session_token(...)で通常のクエリのようにプロシージャコールを処理できるようにしたいと思います。

通常私はRETURN QUERY(...)を使用しますが、私は最初の行を更新する(tokenが主な指標であるので、それが1またはnoneのいずれかです)

しかし、実際には、私は他の基準が満たされた場合、行のみが返され、更新したいです、私は主キーそのものを操作することはできません。

私はRefcursorを使用し、もう1つはSELECT INTOを使用して2回試みましたが、実際にはSETOF usersを返しません。

SELECT INTOと私の試み:cursor

CREATE OR REPLACE FUNCTION use_session_token(Char(128), Inet) RETURNS SETOF users AS $$ 
DECLARE 
    row Record; 
BEGIN 
    SELECT u.* INTO row FROM sessions AS t 
    INNER JOIN users AS u ON (t.user_id = u.id) 
    WHERE 
    t.token=$1 AND 
    t.date_last_used > NOW() - interval '30 minutes' AND 
    t.ip_address=$2 AND 
    u.is_deleted=FALSE AND 
    EXISTS(
     SELECT 1 FROM mail AS m 
     WHERE m.user_id=u.id AND m.is_confirmed=TRUE AND m.is_deleted=FALSE 
    ) 
    ; 

    IF (row) THEN 
    UPDATE sessions SET date_last_used=NOW() WHERE token=$1; 
    ELSE 
    -- maybe do other things if there is no result 
    END IF; 

    RETURN row; 
END; 
$$ LANGUAGE 'plpgsql'; 

私の試み:

CREATE OR REPLACE FUNCTION use_session_token(Char(128), Inet) RETURNS SETOF users AS $$ 
DECLARE 
    cursor Refcursor; 
    row Record; 
BEGIN 
    OPEN cursor SCROLL FOR (
    SELECT u.* INTO row FROM sessions AS t 
    INNER JOIN users AS u ON (t.user_id = u.id) 
    WHERE 
     t.token=$1 AND 
     t.date_last_used > NOW() - interval '30 minutes' AND 
     t.ip_address=$2 AND 
     u.is_deleted=FALSE AND 
     EXISTS(
     SELECT 1 FROM mail AS m 
     WHERE m.user_id=u.id AND m.is_confirmed=TRUE AND m.is_deleted=FALSE 
    ) 
); 

    FETCH cursor INTO row; 

    IF (FOUND) THEN 
    MOVE PRIOR cursor; 
    UPDATE sessions SET date_last_used=NOW() WHERE CURRENT OF cursor; 
    ELSE 
    -- maybe do other things if there is no result 
    END IF; 

    RETURN row; 
END; 
$$ LANGUAGE 'plpgsql'; 

しかし、両方の試みは実際に私は、適切な結果セットを返すことが本当にできないんだということで失敗。

これを達成し、私の問題を解決する最良の方法は何ですか?

両方の試みのどちらが優れているのですか(または3番目の解決策ですか)?

答えて

2

最初の試みは最高です(カーソルは遅くなる傾向があります)が、関数から行を返すにはRETURN NEXTを使用する必要があります。あなたは関係なく、SELECTクエリ(IF FOUND THEN ...節)の後に何が起こるかの選択された行を返す場合は、あなたもrec変数を忘れると、最初の文を書くことができ

CREATE OR REPLACE FUNCTION use_session_token(Char(128), Inet) RETURNS SETOF users AS $$ 
DECLARE 
    rec users%rowtype; -- don't use reserved word as variable name, use explicit type 
BEGIN 
    SELECT u.* INTO rec FROM sessions AS t 
    JOIN users AS u ON t.user_id = u.id 
    WHERE t.token=$1 
    AND t.date_last_used > now() - interval '30 minutes' 
    AND t.ip_address=$2 
    AND NOT u.is_deleted 
    AND EXISTS (
     SELECT 1 FROM mail AS m 
     WHERE m.user_id=u.id AND m.is_confirmed AND NOT m.is_deleted; 

    IF FOUND THEN -- use built-in parameter to test for result of query 
    UPDATE sessions SET date_last_used = now() WHERE token=$1; 
    ELSE 
    -- maybe do other things if there is no result 
    END IF; 

    RETURN NEXT rec; 
END; 
$$ LANGUAGE 'plpgsql';

:他のいくつかの改善点で、あなたはこれを取得しますよう:

RETURN QUERY SELECT u.* ... 

RETURN QUERYが実際に関数から返されないことに注意してください、それは単に、結果セットにデータを追加します。

+0

ありがとう、あなたの答えは本当に私を助けてくれました。関数でレコードが必要ないので、実際には 'RETURN QUERY SELECT ... 'で解決しました。私はRETURNが手順を終了しないことを知らなかった。しかしメモ:それは 'RETURN QUERY ...'であり、 'RETURN QUERY ... 'ではありません。後者は文法エラー – h345k34cr

+0

を助けてくれて、エラーを指摘してくれてありがとう。回答が修正されました。 'RETURN'は実際に関数を終了します。データを返さないだけです。そのためには' RETURN NEXT'または 'RETURN QUERY'が必要です。 – Patrick

関連する問題