这是postgres plpgsql
的{{1}}函数。它尝试9.6
一行,如果插入没有失败(由于键约束违规),那么它会再运行一些命令。
INSERT
此功能处理单个记录,但您如何修改它以处理一批数千条记录?
我找到了一个answer,它建议将3个函数参数中的每一个都作为一个数组。但有没有办法在我传递的参数中更接近地表示记录在我的应用程序中的外观?
例如,理想的解决方案是我的应用程序代码调用CREATE FUNCTION foo(int, text, text)
RETURNS void AS
$$
BEGIN
INSERT INTO table1 (id, val1, val2) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;
IF FOUND THEN
INSERT INTO table2 (table1_id, val1) VALUES ($1, $2);
UPDATE table3 SET (val2, time) = ($3, now()) WHERE table1_id = $1;
END IF;
END
$$
,其中参数select foo($1)
是 JSON对象数组,其中每个内部对象都是一个记录插入。
$1
第二个最佳选项是我的应用程序代码调用[
{ "id": "1", "val1": "1-val1", "val2": "1-val2" },
{ "id": "2", "val1": "2-val1", "val2": "2-val2" },
{ "id": "3", "val1": "3-val1", "val2": "3-val2" },
{ "id": "4", "val1": "4-val1", "val2": "4-val2" }
]
,其中每个参数都是一个 JSON对象,对应于要插入的记录。
select foo($1, $2, $3, $4)
我正在研究Postgres here提供的各种JSON函数,它们似乎与此相关,但我无法弄清楚究竟要使用哪些。我想要做甚么可能吗?是否可以在任何地方使用 JSON数组而不是 JSON对象?
答案 0 :(得分:3)
1。创建一个输入行的临时表,其中包含您的值$1
,$2
,$3
。如果数据不在同一台计算机上,则上传的最快方式是COPY
- 或\copy
meta-command of psql。我们假设这个表:
CREATE TEMP TABLE tmp(id int PRIMARY KEY, val1 text, val2 text);
我添加了一个PK约束,它完全是可选的,但它确保我们处理唯一的非null int值。如果您可以保证输入数据,则不需要约束。
2. 使用数据修改CTE链接您的命令。正如我们根据您的previous question确定的那样,在此特定行动中没有竞争条件。
WITH ins1 AS (
INSERT INTO table1 AS t1 (id, val1, val2)
SELECT id, val1, val2 FROM tmp ON CONFLICT DO NOTHING
RETURNING t1.id, t1.val1, t1.val2 -- only actually inserted rows returned
)
, ins2 AS (
INSERT INTO table2 (table1_id, val1)
SELECT id, val1 FROM ins1
)
UPDATE table3 t3
SET val2 = i.val2
, time = now()
FROM ins1 i
WHERE t3.table1_id = i.id;
步骤1.和2.必须必须在 同一会话 (不一定是同一个事务)中运行,因为临时表的范围绑定到同一个会话
注意,UPDATE
仅取决于第一个INSERT
,第二个INSERT
的成功得到保证,因为没有ON CONFLICT DO NOTHING
并且整个操作将被滚动如果第二个INSERT
中存在任何冲突,请返回。
相关:
有多种选择。您将JSON数组传递给函数的想法就是其中之一。如果对象与目标表匹配,则可以在单个INSERT
查询中使用json_populate_recordset()
。或者只使用INSERT
(作为预处理语句)而不使用函数包装器。
INSERT INTO target_tbl -- it's ok to omit target columns here
SELECT *
FROM json_populate_recordset(null::target_tbl, -- use same table type
json '[{ "id": "1", "val1": "1-val1", "val2": "1-val2" },
{ "id": "2", "val1": "2-val1", "val2": "2-val2" },
{ "id": "3", "val1": "3-val1", "val2": "3-val2" },
{ "id": "4", "val1": "4-val1", "val2": "4-val2" }]');
对于少数几列,您也可以为每列传递一个数组并并行循环。您可以通过数组索引上的简单循环来完成此操作。从Postgres 9.4开始,还有一个方便的unnest()
具有多个参数,可以在一个查询中完成所有操作:
最佳解决方案取决于您拥有的数据格式。