14

我有一个简单的表(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"

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

4

1 回答 1

25

如果您查看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.

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

...以及建议使用特殊排除表插入的行。

因此,您需要访问魔术表 alias excluded,其中包含您尝试插入但发生冲突的值,从而为您提供:

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

如果为此目的弹出一个假想的表名看起来很奇怪,请考虑在编写触发器时会发生类似的事情,您在哪里OLD以及NEW(取决于您正在编写的触发器的类型)。

于 2016-02-02T17:13:43.523 回答