PostgreSQL: 从 JSON 向表中插入数据

18

现在我习惯手动解析json并将其插入到字符串中,如下所示

insert into Table (field1, field2) values (val1, val2)

但从JSON插入数据的方式并不舒适!我发现了函数json_populate_record并尝试使用它:

但使用 json_populate_record 函数插入数据并不方便。

create table test (id serial, name varchar(50));
insert into test select * from json_populate_record(NULL::test, '{"name": "John"}');

但是它失败了,并显示消息:在“id”列中的null值违反了非空约束。PG知道id是序列,但假装自己是个傻瓜。对于所有具有默认值的字段也是如此。

是否有更优雅的方式将JSON数据插入表中?


1
我找到了函数json_populate_record。嗯,它在哪里?这个函数的定义是什么?你是在运行PostgreSQL 9.3 beta,还是从其他地方安装了这个函数? - Craig Ringer
这是9.3 beta版本的函数。 - user2627000
2个回答

18

json_populate_record 很难返回一个表示“生成此值”的标记。

PostgreSQL 不允许您插入 NULL 来指定应该生成的值。如果您请求 NULL,Pg 期望意味着 NULL,并且不想再次猜测。此外,拥有无 NOT NULL 约束的生成列是完全可以的,在这种情况下,往其中插入 NULL 是完全可以的。

如果要让 PostgreSQL 使用表默认值,则有两种方法:

  • INSERT 列表中省略该行;或者
  • 显式写入 DEFAULT,这仅在 VALUES 表达式中有效。

由于您不能在此处使用 VALUES(DEFAULT, ...),因此您唯一的选择是从 INSERT 列表中省略该列:

regress=# create table test (id serial primary key, name varchar(50));
CREATE TABLE
regress=# insert into test(name) select name from json_populate_record(NULL::test, '{"name": "John"}');
INSERT 0 1

是的,这意味着您必须列出所有的列。实际上,需要在SELECT列表和INSERT列列表中列出两次。

要避免这种情况,PostgreSQL需要一种指定记录中DEFAULT值的方法,这样json_populate_record就可以返回DEFAULT而不是对于未定义的列返回NULL。但这可能并不是您对所有列都想要的结果,并且会引出一个问题,即当json_populate_record未在INSERT表达式中使用时,如何处理DEFAULT

因此,我想json_populate_record可能对于具有生成键的行来说比您预期的要少用。


非常酷。有没有一种方法可以根据JSON输入进行“更新”? - jney
@jney,我不太确定你在问什么。我建议您发布一个新的详细问题,并链接回这个问题以提供背景信息。 - Craig Ringer
非常有用的信息(json_populate_record 的文档有点稀少)。但似乎确实有一个很好的理由来添加一个选项,即“在未指定值时使用默认值”。 - beldaz

14

继承Craig's answer,您可能需要编写某种存储过程来执行必要的动态SQL,如下所示:

CREATE OR REPLACE FUNCTION jsoninsert(relname text, reljson text)
  RETURNS record AS
$BODY$DECLARE
 ret RECORD;
 inputstring text;
BEGIN
  SELECT string_agg(quote_ident(key),',') INTO inputstring
    FROM json_object_keys(reljson::json) AS X (key);
  EXECUTE 'INSERT INTO '|| quote_ident(relname) 
    || '(' || inputstring || ') SELECT ' ||  inputstring 
    || ' FROM json_populate_record( NULL::' || quote_ident(relname) || ' , json_in($1)) RETURNING *'
    INTO ret USING reljson::cstring;
  RETURN ret;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;

你随后将使用以下方式进行调用

SELECT jsoninsert('test', '{"name": "John"}');

这正是我一直在寻找的!在搜索了许多小时后终于找到了。如果我想要返回值以JSON对象形式呈现,就像输入值一样,应该如何实现? - David Tedman-Jones
1
很高兴能帮到你。如果你想返回JSON,我猜可以改成RETURNS json,然后用RETURN row_to_json(ret)代替RETURN ret。虽然我没有测试过,但这是一般的想法。 - beldaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接