你得到的错误:
ON ConFLICT DO UPDATe命令不能再次影响行
表示你试图在单个命令中多次向上插入同一行。换句话说:你
(name, url, email)的
VALUES名单上有骗子。折叠重复项(如果可以的话),它应该可以工作。但是你将不得不决定从每组重复中选择哪一行。
INSERT INTO feeds_person (created, modified, name, url, email)SELECT DISTINCT ON (name, url, email) *FROM ( VALUES ('blah', 'blah', 'blah', 'blah', 'blah') -- ... more ) v(created, modified, name, url, email) -- match column listON ConFLICT (name, url, email) DO UPDATeSET url = feeds_person.urlRETURNING id;由于我们现在使用独立VALUES表达式,因此你必须为非默认类型添加显式类型转换。喜欢:
VALUES (timestamptz '2016-03-12 02:47:56+01' , timestamptz '2016-03-12 02:47:56+01' , 'n3', 'u3', 'e3') ...
你的timestamptz列需要显式类型转换,而字符串类型可以使用default操作text。(你仍然可以立即投射varchar(n)。)
有一些方法可以确定从每个重复对象中选择哪一行:
在每个GROUP BY组中选择第一行?
没错,(当前)没有办法在子句中获取排除的行
RETURNING。我引用Postgres Wiki:
请注意,RETURNING这不会使“
EXCLUDED.*”别名可见UPDATE(只有通用的“
TARGET.*”别名在此处可见)。人们认为这样做会为简单,常见的情况[30]带来烦人的歧义,却几乎没有好处。在将来的某个时候,我们可能会寻求一种方法来公开是否
RETURNING插入和更新了-projected元组,但这可能不需要使它成为该功能的首次提交迭代[31]。
但是,你不应该更新不应更新的行。空更新几乎与常规更新一样昂贵-可能会有意想不到的副作用。你并非一开始就完全需要UPSERT,你的案例看起来更像是“ SELECT或INSERT”。有关:
SELECT或INSERT函数是否易于出现竞争状况?
插入一组行的一种更干净的方法是使用修改数据的CTE:
WITH val AS ( SELECT DISTINCT ON (name, url, email) * FROM ( VALUES (timestamptz '2016-1-1 0:0+1', timestamptz '2016-1-1 0:0+1', 'n', 'u', 'e') , ('2016-03-12 02:47:56+01', '2016-03-12 02:47:56+01', 'n1', 'u3', 'e3') -- more (type cast only needed in 1st row) ) v(created, modified, name, url, email) ), ins AS ( INSERT INTO feeds_person (created, modified, name, url, email) SELECt created, modified, name, url, email FROM val ON ConFLICT (name, url, email) DO NOTHING RETURNING id, name, url, email )SELECt 'inserted' AS how, id FROM ins -- insertedUNIOn ALLSELECt 'selected' AS how, f.id -- not insertedFROM val vJOIN feeds_person f USING (name, url, email);增加的复杂性应该为大表支付INSERT规则和SELECT例外。
最初,我NOT EXISTS在最后一个谓词上添加了谓词,SELECT以防止结果重复。但这是多余的。单个查询的所有CTE都可以看到表的相同快照。返回ON ConFLICT (name, url, email) DO NOTHING的集合INNER JOIN与同一列上的之后返回的集合互斥。
不幸的是,这也为比赛条件打开了一个很小的窗口。如果…
- 并发事务插入冲突的行
- 尚未承诺
- 但最终承诺
…某些行可能会丢失。
你可能只是INSERT .. ON ConFLICT DO NOTHING,紧接着是SELECT对所有行的单独查询-在同一个事务内即可解决此问题。这反过来又打开另一个小窗户的竞争条件,如果并发事务可以承诺之间写入表INSERT和SELECT(在默认的READ COMMITTED隔离级别)。可以通过REPEATABLE READ事务隔离(或更严格)来避免。或者在整个表上使用(可能很昂贵,甚至是不可接受的)写锁。你可以获得所需的任何行为,但可能需要付出一定的代价。



