2016-04-27 992 views
16

我想使用插入..上的confict做更新..語法與表中有兩列的唯一約束。這可能嗎?Postgres衝突處理與多個唯一的約束

例如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; 

錯誤:衝突DO更新需要推理規範或約束名

這也不會工作:

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?暗指,但不提供語法。

+0

恕我直言,這個問題沒有任何意義。在'衝突簡單案例'中:密鑰被保留,並且(一些)相關字段被更新。就你而言,你打算更新另一個(候選)密鑰。事實上,你試圖更新兩個(候選)鍵,這超出了我的邏輯。 – wildplasser

+0

我已經更新了示例以使其更加真實。這個想法是保持一個與任一唯一列匹配的計數器列是最新的,或者如果兩者都不存在,則插入零。 –

+2

添加一個額外的約束'UNIQUE(col1,col2)'可能會做你想做的。 (它在邏輯上是多餘的,但數據模型無論如何沒有多少意義或沒有意義) – wildplasser

回答

14

當我們要求DO UPDATE時,ON CONFLICT子句需要一個唯一的約束。當定義主鍵時,僅引用列名就足夠了;這是人們傾向於發現的主要例子。

你提到你有「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

別急,還有更多...

The idea is to keep a counter column up to date which matches on either unique column, or insert zero if neither exists...

這比你服用這裏是一個不同的路徑。 「在唯一列上的匹配」允許兩者匹配,或者兩者都不匹配。如果我理解你的意圖,只需要一個標籤並在適用的記錄上增加計數器。所以:

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;