在postgres上使用什么代替“ INSERT ... ON CONFLICT DO NOTHING”

Clé*_*ine 6 postgresql

我有以下查询,与postgres 9.5一起使用:

INSERT INTO knowledge_state 
(SELECT learnerid learner_id, lo_id FROM qb_lo_tag WHERE qb_id = NEW.qb_id)
ON CONFLICT DO NOTHING ;
Run Code Online (Sandbox Code Playgroud)

不幸的是,我不能在某些服务器上使用postgres 9.5,我需要将其转换为9.5之前的友好查询。我建立了以下查询,但对我来说似乎复杂得多,我认为可能有更简单的方法。

FOR rows IN SELECT lo_id FROM knowledge_state 
WHERE learner_id = learnerid 
AND lo_id IN (SELECT lo_id FROM qb_lo_tags WHERE qb_id = New.qb_id) LOOP

  INSERT INTO knowledge_state (lo_id, learner_id) SELECT rows.lo_id, learnerid 
WHERE NOT EXISTS (SELECT * FROM knowledge_state WHERE lo_id = rows.lo_id AND learner_id = learnerid);

END LOOP;
Run Code Online (Sandbox Code Playgroud)

我很想听听有关如何简化此查询的想法。

tpd*_*pdi 6

只要做你正在做的事情,没有循环:

INSERT INTO knowledge_state (lo_id, learner_id) 
SELECT  a.lo_id, a.learnerid
FROM qb_lo_tag a
WHERE a.qb_id = NEW.qb_id
and  NOT EXISTS (SELECT * FROM knowledge_state b 
     WHERE b.lo_id = a.lo_id AND b.learner_id = a.learnerid);
Run Code Online (Sandbox Code Playgroud)

当然,你可以在knowledge_state(lo_id, learner_id)上添加索引,以使其更快(On Conflict意味着唯一约束或其他约束,唯一约束意味着索引)。

  • @Clémentine 不。想象一下,如果两个并发会话*同时*都执行了“INSERT ... WHERE NOT EXISTS (SELECT ...)”。对于它们的“两个”来说,“WHERE NOT EXISTS”将为“true”,因为它们都找不到行。所以两者都会继续“INSERT”。一个人会失败。这是事务可见性规则(特别是防止脏读)的结果,再加上唯一索引部分违反事务隔离以确保唯一性的事实。您*必须*要么 **锁定表** 要么 **使用 plpgsql 循环来捕获 unique_violation 并重试**。 (3认同)