在 PostgreSQL 函数中基于 JSON 更新表

Posted

技术标签:

【中文标题】在 PostgreSQL 函数中基于 JSON 更新表【英文标题】:Updating table based on JSON inside PostgreSQL function 【发布时间】:2021-12-15 23:47:49 【问题描述】:

我正在编写一个 plpgsql 函数,它应该根据提供的 JSON 对象更新表。 JSON 包含一个表表示形式,其中包含与表本身相同的所有列。

函数目前看起来如下:

CREATE OR REPLACE FUNCTION update (updated json)

BEGIN

/* transfrom json to table */
WITH updated_vals AS (
    SELECT
        *
    FROM
        json_populate_recordset(NULL::my_table, updated)
),

/* Retrieve all columns from mytable and also with reference to updated_vals table */
cols AS (
    SELECT
        string_agg(quote_ident(columns), ',') AS table_cols,
        string_agg('updated_vals.' || quote_ident($1), ',') AS updated_cols
    FROM
        information_schema
    WHERE
        table_name = 'my_table' -- table name, case sensitive
        AND table_schema = 'public' -- schema name, case sensitive
        AND column_name <> 'id' -- all columns except id and user_id
        AND column_name <> 'user_id'
),

/* Define the table columns separately */
table_cols AS (
    SELECT
        table_cols
    FROM
        cols
),

/* Define the updated columns separately */
updated_cols AS (
    SELECT
        updated_cols
    FROM
        cols)

/* Execute the update statement */
    EXECUTE 'UPDATE my_table'
    || ' SET (' || table_cols::text || ') = (' || updated_cols::text || ') '
    || ' FROM updated_vals '
    || ' WHERE my_table.id = updated_vals.id '
    || ' AND my_table.user_id = updated_vals.user_id';

COMMIT;
END;

我注意到WITH 子句与EXECUTE 的组合总是会触发错误syntax error at or near EXECUTE,即使它们非常简单明了。是否确实如此,如果是这样,将所需变量(updated_valstable_colsupdated_cols)提供给EXECUTE 的替代方法是什么?

如果您对此代码有任何其他改进,我很高兴看到这些改进,因为我对 sql/plpgsql 非常陌生。

【问题讨论】:

【参考方案1】:

如果您在函数中写入表名 (my_table),这意味着您将始终只更新 JSON 数据中的一个指定表。因此,您可以手动在函数中写入表名和列名,而不是使用information_schema。这是简单易行的方法。

例如:

CREATE OR REPLACE FUNCTION rbac.update_users_json(updated json)
RETURNS boolean 
LANGUAGE plpgsql
AS $function$
begin

    update rbac.users usr 
    set 
        username    = jsn.username, 
        first_name  = jsn.first_name, 
        last_name   = jsn.last_name
    from (
        select * from json_populate_recordset(NULL::rbac.users, updated)
    ) jsn  
    where jsn.id = usr.id;
    
    return true;    
   
END;
$function$
;

【讨论】:

您确实正确地注意到我写了my_table。然而,我提供的代码是真实代码的程式化示例。 my_table 将替换为动态表。【参考方案2】:

对于动态表:

CREATE OR REPLACE FUNCTION rbac.update_users_json_dynamic(updated json)
RETURNS boolean  
LANGUAGE plpgsql
AS $function$
declare
    f record;
    exec_sql text;
    sep text;
begin

    exec_sql = 'update rbac.users usr set ' || E'\n';
    sep = '';

    for f in 
        select clm.column_name 
        from 
            information_schema."tables" tbl
        inner join 
            information_schema."columns" clm on 
                        clm.table_name = tbl.table_name 
                        and clm.table_schema = tbl.table_schema 
        where 
            tbl.table_schema = 'test' 
            and tbl.table_name = 'users'
            and clm.column_name <> 'id'
    loop 
        exec_sql = exec_sql || sep || f.column_name || ' = ' || 'jsn.' || f.column_name;
        sep = ', ' || E'\n';
    end loop;
   
    exec_sql = exec_sql || E'\n' || 'from (select * from json_populate_recordset(NULL::rbac.users, ''' || 
                updated::text || ''')) jsn ' || E'\n' || 'where jsn.id = usr.id';
    
    execute exec_sql;

    return true; 
   
END;
$function$
;

【讨论】:

以上是关于在 PostgreSQL 函数中基于 JSON 更新表的主要内容,如果未能解决你的问题,请参考以下文章

Postgres-XL:JSON 函数

如何在 jOOQ 中转换“to_json()”PostgreSQL 函数?

在 PostgreSQL 中编写函数时出错

PostgreSQL 15 JSON 函数增强

在 postgresql 中识别 json 类型

PostgreSQL:json中的词汇概述,窗口函数......递归?