ON CONFLICT DO UPDATE缺少FROM子句。

15

我有一个简单的表格(id和name列,都是唯一的),我正在导入一个制表符分隔的CSV文件。

我正在运行psql 9.5,并想尝试使用新的ON CONFLICT功能,如果ID已经存在,则更新name列。

CREATE TEMP TABLE tmp_x AS SELECT * FROM repos LIMIT 0;
COPY tmp_x FROM '/Users/George/git-parser/repo_file' (format csv, delimiter E'\t');
INSERT INTO repos SELECT * FROM tmp_x
ON CONFLICT(name) DO UPDATE SET name = tmp_x.name;
DROP TABLE tmp_x;

我遇到了这个错误:
SELECT 0
COPY 1
ERROR:  missing FROM-clause entry for table "tmp_x"
LINE 4: ON CONFLICT(name) DO UPDATE SET name = tmp_x.name;
                                               ^
Query failed
PostgreSQL said: missing FROM-clause entry for table "tmp_x"

我不太确定这里出了什么问题。

1个回答

27

如果您查看"ON CONFLICT"语句的文档,它关于"冲突操作"的部分是这样描述的:

在ON CONFLICT DO UPDATE中,SET和WHERE子句可以使用表名(或别名)访问现有行。

在您的查询中,目标表是repos

然而,tmp_x是您要插入数据的来源,但是ON CONFLICT语句无法"看到"它——它正在查看计算并失败的特定行。考虑一下,如果您写了这样的东西:

INSERT INTO repos SELECT max(foo_id) FROM tmp_x

显然,如果一行数据无法插入到 repos 表中,那么它就不应该能够访问来自 tmp_x 表的任何一行。

如果没有办法查看被拒绝的数据,那么整个功能就毫无用处。但是,如果我们继续阅读:

... 以及使用特殊排除表提出的行。

因此,您需要访问魔术表别名 excluded,该表包含您尝试插入但出现冲突的值,给您提供了这个:

INSERT INTO repos SELECT * FROM tmp_x
ON CONFLICT(name) DO UPDATE SET name = excluded.name;

如果出现一个虚构的表名似乎很奇怪,考虑一下当编写触发器时会发生类似的事情,你会得到OLDNEW(取决于你正在编写的触发器类型)。


请注意,在第二个示例中最好使用 ON CONFLICT DO NOTHING(虽然我理解 OP 可能不想这样做)。 - dwelle
1
我刚遇到了这个问题,没有太大的希望在谷歌上搜索。很高兴能够找到你的答案。谢谢! - Eloff

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