我正在阅读一个很大的数据列表,并将其添加到PostgreSQL数据库中。问题是,有时我正在读取的数据中有重复的值,但有时它们会填充以前丢失的数据。为了解决这个问题,我在脚本中添加了以下内容,但它非常丑陋:
INSERT INTO tab(id,col1,col2,col3,...) VALUES (i,v1,v2,v3,...)
ON CONFLICT (id)
DO UPDATE
SET
(col1,col2,col3, ...)=(
COALESCE(tab.col1, EXCLUDED.col1),
COALESCE(tab.col2, EXCLUDED.col2),
COALESCE(tab.col3, EXCLUDED.col3),
...
);
我希望有一个比手动写出表中的每一列更优雅的解决方案。我还有几个表需要写这些,所以我希望有一个更通用的方法来完成这一点。
编辑:我是个新手,阅读文档,这可能是一个愚蠢的方式来做到这一点摆在首位。请让我知道,如果我甚至应该使用INSERT
命令,它看起来像也许只是UPDATE
或某种形式的JOIN
可以完成同样的事情?
Postgres版本:psql (PostgreSQL) 12.14 (Ubuntu 12.14-0ubuntu0.20.04.1)
2条答案
按热度按时间3xiyfsfu1#
需要列出每一列,但不需要手动输入列表。以下查询是使用information_schema中的列信息生成SET子句的示例:
这可以很容易地合并到一个函数中,并扩展为为每个表生成整个插入查询。
avwztpqn2#
你的查询看起来很好,构建它的John的元查询也是如此。
一个主要问题仍然存在:不要更新那些实际上没有改变的行。这样会增加全部更新成本,但没有任何收益。
更好(但更详细):
第一个建议仅在输入行与现有行完全相同的情况下禁止更新。
第二个(better)建议会抑制所有空更新。
相关:
上级法
我上面的建议有助于减少昂贵的更新数量。
如果您可以在不增加太多开销的情况下进行管理,请完全不要重复更新同一行。在应用
UPDATE
之前,将多个输入行合并为一行。例如:
你可以先写一个
TEMPORARY
staging tablemy_input_rows
,然后从那里获取它。或者像初始代码一样使用VALUES
表达式,只是不直接附加到INSERT
,所以你可能需要显式的类型转换。请参阅:我选择了
min()
,因为它忽略了null
输入。如果你有null
或每个集合有 * 一个 * 不同的值,那就可以了。甚至可以在一个SQL语句中对多个目标表执行此操作,使用多个数据修改CTE。