PostgreSQL如何实现save日期不同时允许同userid插入的逻辑?
问题解答
0. 两种现有写法是否可行?
两种写法都不可行,错误原因分别如下:
- INSERT 0错误点:
ON CONFLICT后指定的冲突键必须和已存在的唯一索引完全匹配,你建的是(userid, save)联合唯一索引,写ON CONFLICT (userid)无法匹配到该索引,不会触发冲突逻辑。- PostgreSQL不支持
ON CONFLICT DO INSERT语法,冲突触发后仅支持DO NOTHING或DO UPDATE两种操作。 - WHERE条件中直接混用
table01.save和save属于非法引用,无法明确字段归属。
- INSERT 1错误点:
原生INSERT ... VALUES不支持直接追加WHERE子句,你没有指定数据源的情况下直接引用table01.save,数据库无法确定需要和表中哪一行的save字段做比对,语法不合法。
1. Postgres是否支持ON CONFLICT DO INSERT语法?
不支持。ON CONFLICT是插入行触发唯一约束/排他约束冲突后的回调逻辑,冲突本身就是当前插入行导致的,不存在冲突后再插入一次的逻辑,合法操作仅两种:
DO NOTHING:直接跳过当前冲突行,不做任何修改DO UPDATE:更新冲突命中的已有行的指定字段
2. Postgres是否支持INSERT INTO ... WHERE语法?
不支持直接给INSERT ... VALUES加WHERE子句。如果需要插入前做条件判断,可以用INSERT ... SELECT的写法,在SELECT子句后加WHERE条件,从临时结果集中筛选符合要求的行再插入。
正确实现方案
你的需求本质是仅当(userid, save)组合完全重复时才拒绝插入,其余场景(包括同一userid不同save的情况)都允许插入,这刚好和你创建的(userid, save)联合唯一索引的特性完全匹配,直接用以下写法即可:
INSERT INTO table01 (userid, save, followers) VALUES (%s, %s, %s) ON CONFLICT (userid, save) DO NOTHING;
如果需要兼容更低版本或者需要自定义判断逻辑,也可以用INSERT ... SELECT + NOT EXISTS的等效写法,高并发场景下更推荐上面的ON CONFLICT方案,避免幻读导致的唯一约束报错:
INSERT INTO table01 (userid, save, followers) SELECT v.userid, v.save, v.followers FROM (VALUES (%s::int8, %s::date, %s::int4)) AS v(userid, save, followers) WHERE NOT EXISTS ( SELECT 1 FROM table01 t WHERE t.userid = v.userid AND t.save = v.save );
内容的提问来源于stack exchange,提问作者étale-cohomology
相关产品推荐
相关产品推荐

