使用多个唯一约束进行Postgres冲突处理

时间:2016-04-27 09:46:13

标签: postgresql

我想使用insert .. on confict do update ..语法与一个对两列有唯一约束的表。这可能吗?

e.g。 mytable对col1和col2有独立的唯一约束。

我可以写:

INSERT INTO mytable(col1, col2, col3) values ('A', 'B', 0) ON CONFLICT DO NOTHING;

然而,这不起作用:

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0) 
ON CONFLICT 
DO UPDATE SET col3 = EXCLUDED.col3 + 1;

ERROR:ON CONFLICT DO UPDATE需要推理规范或约束名称

这也不起作用:

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0)
ON CONFLICT (col1, col2) 
DO UPDATE SET col3 = EXCLUDED.col3 + 1;

错误:没有与ON CONFLICT规范匹配的唯一或排除约束

这种语法似乎是针对两列的单个复合唯一约束而设计的,而不是两个约束。

如果违反任何一个唯一约束,有没有办法进行条件更新?这个问题How to upsert in Postgres on conflict on one of 2 columns?暗示了它,但没有提供语法。

2 个答案:

答案 0 :(得分:25)

ON CONFLICT子句在我们要求DO UPDATE时需要一个唯一约束。定义主键时,仅引用列名就足够了;这是人们倾向于找到的主要例子。

您提到您已对col1和col2'分隔了唯一约束,因此我可能会假设您的表定义与此类似:

CREATE TABLE mytable(       
    col1 varchar UNIQUE,    
    col2 varchar UNIQUE,    
    col3 int
);

但您的查询引用了复合约束;而不是单独的约束。修改后的表定义如下:

CREATE TABLE mytable2(  
    col1 varchar UNIQUE,
    col2 varchar UNIQUE,
    col3 int,
    CONSTRAINT ux_col1_col2 UNIQUE (col1,col2)
);

可以使用上面的查询:

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0)
ON CONFLICT (col1, col2) 
DO UPDATE SET col3 = EXCLUDED.col3 + 1;

您可以将此唯一约束引用为ON CONFLICT (col1, col2)ON CONFLICT ON CONSTRAINT ux_col1_col2

但等等,还有......

  

这个想法是保持一个与之匹配的最新的计数器列   要么是唯一列,要么如果两者都不存在则插入零...

这与你在这里采取的路径不同。 " 匹配任一列"允许在两者上,两者之一或两者之间进行匹配。如果我理解你的意图,只需要一个标签并增加适用记录上的计数器。所以:

CREATE TABLE mytable2(  
    col1 varchar PRIMARY KEY,
    col3 int
);
INSERT INTO mytable2(col1,col3)
SELECT incr_label,0
FROM (VALUES ('A'),('B'),('C')) as increment_list(incr_label)
ON CONFLICT (col1)
DO UPDATE SET col3 = mytable2.col3 + 1
RETURNING col1,col3;

答案 1 :(得分:1)

因为 conflict_target 不能是两个不同的唯一约束,所以您必须使用模拟的 upsert 并自己处理冲突。

<块引用>

-- 需要

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0) 关于冲突 DO UPDATE SET col3 = EXCLUDED.col3 + 1;

WITH upsert AS (
  UPDATE mytable
  SET col1 = 'A', col2 = 'B', col3 = col3 + 1
  WHERE col1 = 'A' OR col2 = 'B'
  RETURNING *
)
INSERT INTO mytable (col1, col2, col3)
SELECT 'A', 'B', 0
WHERE NOT EXISTS (SELECT * FORM upsert);

此语句将导致包含 A 或 B 或两者的行,换句话说,col1 上的唯一性和 col2 上的唯一性得到满足。

不幸的是,此解决方案受到 A 和 B 之间必须存在某种逻辑链接的限制,否则如果插入 ('A', null),然后是 (null, B),然后是 (A, B),您将结束最多两行,都由第三个插入增加:

| col1 | col2 | col3 |
+------+------+------+
|    A | null |    1 |
| null |    B |    1 |