【问题标题】:Returning multiple values from UPDATE query in PostgreSQL从 PostgreSQL 中的 UPDATE 查询返回多个值
【发布时间】:2015-10-10 22:50:44
【问题描述】:

我是编写数据库函数的新手,在执行UPDATE 查询时,我需要将“last_login_at”的值作为OUT 参数返回。

这是我的函数的 sn-p:

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

可以做多个RETURNING x INTO y吗?
我们可以在RETURNING x INTO y 中使用CASE 语句吗?

编辑

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

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

唯一的问题是RETURNING 处的last_login_at 已经更新,所以CASE 总是返回TRUE

我的问题有什么神奇的解决方案吗?

【问题讨论】:

    标签: sql postgresql sql-update plpgsql sql-returning


    【解决方案1】:

    我的问题有什么神奇的解决方案吗?

    其实有:在FROM子句中加入"user"表的另一个实例:

       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;

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

    详细解释:

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

    The manual about the short syntax EXIT WHEN FOUND.

    您可以在RETURNING 子句中使用任何表达式,包括CASE 语句。碰巧有一种更简单、更便宜的方法:

    CASE WHEN date_part('day', age(current_timestamp, last_login_at)) = 0
    THEN true ELSE false END
    

    第 1 步:

    CASE WHEN last_login_at < now() + interval '1 day'
    THEN true ELSE false END
    

    第 2 步:

    (last_login_at < now() + interval '1 day')
    

    只需使用boolean 结果。如果last_login_atNULL,则得到NULL


    旁白:
    至于查询的其余部分:表达式可以简化,LOOP 是可疑的,你应该永远使用reserved words 作为标识符,即使双引号可以实现(@ 987654341@),该算法似乎依赖于以精确 24 小时间隔执行,这很容易出错。

    【讨论】:

    • 哇!!我印象深刻!非常感谢您的详细回答 - 我从您那里学到了新的有用的东西!
    • plpgsql中是否有“+=”运算符?
    • @Shvalb:不,没有。必须是x := x + 1
    • @ErwinBrandstetter 的任何评论都是 postgres 生态系统中的金矿
    【解决方案2】:

    您可以使用以下语法返回多个列:

    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, last_login_at 
            INTO v_user_id, v_login_at;
    

    returning 子句遵循 SELECT 字段列表的大部分规则,因此您可以根据需要添加任意数量的列。

    Update docs

    【讨论】:

    • 我可以使用 "CASE, WHEN , END" 来设置值吗?
    • 我可以这样做:返回 id,CASE WHEN x THEN yELSE z INTO v_user_id,v_login_at;这可能吗?
    • 是的,它正在工作但是有一个问题:我真正感兴趣的是在查询更新之前访问“last_login_at”的值,然后进行计算并使用 OUT 参数返回结果。有没有办法在不进行显式 SELECT 的情况下访问“last_login_at”的值?
    • 在 sqlfiddle.com 上发布您在小提琴中获得的内容,让人们更容易看到您在做什么。
    • sqlfiddle可以添加函数吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 2019-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多