使用 JSON 数组更新 Postgres 表

uns*_*age 2 postgresql database-design stored-procedures

我正在使用 Postgres 9.5,我想弄清楚如何使用 JSON 数组更新 postgres 表。我希望数组中的每个对象都对应一个新行,每个键对应一个列,每个值都是要插入到该列中的数据。我试图用一个函数来做到这一点。这里的数据格式:

[
    { col1: a, col2: 5, col3: 1, col4: one},
    { col1: b, col2: 6, col3: 2, col4: two},
    { col1: c, col2: 7, col3: 3, col4: three},
    { col1: d, col2: 8, col3: 4, col4: four},
]
Run Code Online (Sandbox Code Playgroud)

这是我的预期输出:

 col1   (varchar)| col2 (integer) |   col3 (integer)   |   col4 (varchar)
-----------------+----------------+--------------------+------------------
    a            |  5             |     1              |    one
    b            |  6             |     2              |    two
    c            |  7             |     3              |    three
    d            |  8             |     4              |    four 
Run Code Online (Sandbox Code Playgroud)

是否有内置的 postgres JSON 函数或运算符可以为我执行此操作?或者我是否必须遍历数组并拉出每个值,然后将其作为输入传递?我知道下面的函数是错误的,但我的目标是使函数的行为类似于以下内容:

CREATE OR REPLACE FUNCTION UPDATE_TABLE_FUNC (
arrayOfValues TEXT[]
)
RETURN VOID AS $$
BEGIN
UPDATE table SET (col1, col2, col3, col4) = ($1) 
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

dez*_*zso 5

如果您知道所需的列名(与 JSON 结构中的键相同或不同,您可以使用json[b]_to_recordset()

SELECT * FROM jsonb_to_recordset('[
    { "col1": "a", "col2": 1, "col3": 1, "col4": "one"},
    { "col1": "b", "col2": 2, "col3": 2, "col4": "two"},
    { "col1": "c", "col2": 3, "col3": 3, "col4": "three"},
    { "col1": "d", "col2": 4, "col3": 4, "col4": "four"}
]'::jsonb) AS t (col1 text, col2 integer, col3 integer, col4 text);

 col1 ? col2 ? col3 ? col4  
????????????????????????????
 a    ?    1 ?    1 ? one
 b    ?    2 ?    2 ? two
 c    ?    3 ?    3 ? three
 d    ?    4 ?    4 ? four
Run Code Online (Sandbox Code Playgroud)

正如文档告诉我们的那样,

注意:json_populate_recordjson_populate_recordsetjson_to_record和 中json_to_recordset,来自 JSON 的类型强制是“尽力而为”,对于某些类型可能不会产生所需的值。JSON 键与目标行类型中的相同列名匹配。未出现在目标行类型中的 JSON 字段将从输出中省略,不匹配任何 JSON 字段的目标列将简单地为NULL.

如果您已经有一张桌子可以使用,这json_populate_recordset()是一个更好的解决方案:

CREATE TABLE inputtable (col1 text, col2 integer, col3 integer, col4 text);

SELECT * FROM jsonb_populate_recordset(NULL::yourtable, '[
    { "col1": "a", "col2": 1, "col3": 1, "col4": "one"},
    { "col1": "b", "col2": 2, "col3": 2, "col4": "two"},
    { "col1": "c", "col2": 3, "col3": 3, "col4": "three"},
    { "col1": "d", "col2": 4, "col3": 4, "col4": "four"}
]'::jsonb);

 col1 ? col2 ? col3 ? col4  
????????????????????????????
 a    ?    1 ?    1 ? one
 b    ?    2 ?    2 ? two
 c    ?    3 ?    3 ? three
 d    ?    4 ?    4 ? four
Run Code Online (Sandbox Code Playgroud)

现在更新表本身可以这样完成:

WITH source AS (SELECT * FROM jsonb_populate_recordset [...])
UPDATE yourtable
   SET col1 = s.col1, col2 = s.col2
  FROM source AS s
 WHERE col3 = s.col3;
Run Code Online (Sandbox Code Playgroud)

如果它看起来很慢,那么使用 CTE,而是在FROM子句中使用子查询可能是有意义的。