从PostgreSQL中的UPDATE查询返回多个值

Shv*_*alb 3 sql postgresql plpgsql sql-update sql-returning

我是编写DB函数的新手,我需要OUT在执行UPDATE查询时返回'last_login_at'的值作为参数.

这是我的功能片段:

...
LOOP
    UPDATE "user" SET 
        last_login_at = current_timestamp, 
        first_name = p_first_name,
        last_name = p_last_name,
    WHERE ext_user_id = p_ext_user_id AND platform_id = p_platform_id
    RETURNING id INTO v_user_id;
    is_new := false;
    // The next 'CASE' is not valid - Need to replace it with a valid one.  
    has_logged_in_today = CASE
     WHEN date_part('day', age(current_timestamp, last_login_at)) > 1
     THEN true
     ELSE false
     END;
    IF FOUND THEN 
        EXIT;
    END IF;
..
..
END LOOP;
Run Code Online (Sandbox Code Playgroud)

可以做多个RETURNING x INTO y吗?
我们可以使用CASE声明RETURNING x INTO y吗?

编辑

我能够获得更好的结果,现在它看起来像这样:

   ...
    LOOP
        UPDATE "user" SET 
            login_consecutive_days = CASE 
                WHEN date_part('day', age(current_timestamp, last_login_at)) > 1 
                THEN 0
                ELSE login_consecutive_days + date_part('day', age(current_timestamp, last_login_at))
                END,
        login_max_consecutive_days = CASE
        WHEN date_part('day', age(current_timestamp, last_login_at)) = 1
             AND (login_consecutive_days+1 > login_max_consecutive_days)
        THEN login_consecutive_days+1
        ELSE login_max_consecutive_days
        END,
        last_login_at = current_timestamp, 
            num_sessions = num_sessions + 1,
            last_update_source = 'L',
            first_name = p_first_name,
            last_name = p_last_name,
            additional_data = p_additional_data
        WHERE ext_user_id = p_ext_user_id AND platform_id = p_platform_id
        RETURNING id,
        CASE
        WHEN date_part('day', age(current_timestamp, last_login_at)) = 0
        THEN true
        ELSE false
        END
    INTO v_user_id, is_first_login_today;
        is_new := false;
        IF FOUND THEN 
            EXIT;
        END IF;
    ...
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,在已经更新的那一点上RETURNING,last_login_at所以CASE总是返回TRUE.

我的问题有一个神奇的解决方案吗?

Erw*_*ter 5

我的问题有一个神奇的解决方案吗?

实际上,有:连接到子句"user"中表的另一个实例FROM:

   UPDATE "user" u
   SET    login_consecutive_days = ...  -- unqualified column name

   FROM   "user" u1
   WHERE  u.ext_user_id = p_ext_user_id
   AND    u.platform_id = p_platform_id
   AND    u.id = u1.id                  -- must be unique not null (like the PK)
   RETURNING u.id, (u1.last_login_at < now() + interval '1 day')
   INTO   v_user_id, is_first_login_today;

   is_new := false;
   EXIT WHEN FOUND;
Run Code Online (Sandbox Code Playgroud)

现在,表别名u是指表的后UPDATE状态,但是u1指的是查询开始时的快照.

详细说明:

表限定所有列引用都是明确的,这绝不是一个坏主意,但在自连接之后它是必需的.

关于短语法的手册EXIT WHEN FOUND.

您可以使用RETURNING子句中的任何表达式,包括CASE语句.恰好有一种更简单,更便宜的方式:

CASE WHEN date_part('day', age(current_timestamp, last_login_at)) = 0
THEN true ELSE false END
Run Code Online (Sandbox Code Playgroud)

步骤1:

CASE WHEN last_login_at < now() + interval '1 day'
THEN true ELSE false END
Run Code Online (Sandbox Code Playgroud)

第2步:

(last_login_at < now() + interval '1 day')
Run Code Online (Sandbox Code Playgroud)

只需使用boolean结果.如果last_login_atNULL,你得到NULL.


旁白:
对于其余查询:表达式可以简化,该LOOP可疑,你应该永远不会使用保留字作为标识符,即使双引号能够("user"),该算法似乎取决于执行确切 24小时间隔,这是容易出错的.

  • 哇!!我印象深刻!非常感谢您的详细回答 - 我从您那里学到了新的有用的东西! (2认同)