2015-02-08 32 views
1

這怎麼可能發生?使用SELECT NOT EXISTS INSERT時出錯重複鍵

IntegrityError: (IntegrityError) duplicate key value violates unique constraint "r_u_pkey" 
DETAIL: Key (r_id, u_id)=(2660, 10182) already exists. 
'INSERT INTO r_u(r_id, u_id) SELECT %s, %s WHERE NOT EXISTS (
    SELECT 1 FROM r_u WHERE r_id = %s AND u_id = %s 
)' (2660, 10182, 2660, 10182) 

上有(r_id, u_id)主鍵:

CREATE TABLE r_u 
( 
    r_id integer NOT NULL, 
    u_id integer NOT NULL, 
    CONSTRAINT r_u_pkey PRIMARY KEY (r_id, u_id) 
) 

服務器是9.3.5的Postgres和連接已自動提交的。

+0

http://sqlfiddle.com/#!15/5ccfd/5 - 例如@ SqlFiddle – 2015-02-09 14:25:58

回答

1

嘛,我發現原因及解決辦法。 INSERT SELECT不是原子的。在自動提交模式下,必須使用顯式鎖定在交易:

BEGIN; LOCK TABLE r_u IN SHARE ROW EXCLUSIVE MODE; INSERT INTO r_u(r_id, u_id) SELECT %s, %s WHERE NOT EXISTS ( SELECT 1 FROM r_u WHERE r_id = %s AND u_id = %s ) COMMIT;

來源:http://www.the-art-of-web.com/sql/upsert/

0

如果2660, 10182元組不存在,則您的內部查詢將返回true中的每一行。如果表格中有多行,你會得到上述錯誤。

相反,一個更簡潔的方法可以使用except操作:

INSERT INTO r_u(r_id, u_id) 
SELECT %s, %s 
FROM r_u 
EXCEPT 
SELECT r_id, u_id 
FROM r_u 
+0

號注意EXISTS (或NOT EXISTS)運營商。它測試子查詢中是否存在任何記錄(或不存在)。如果沒有記錄,應該可以插入,但有時會失敗。 你的答案可能會奏效,但我認爲UNION會比較慢,只需在主鍵上進行簡單的選擇即可。 – 2015-02-09 13:49:39

+0

@PavelFrancírek否,重新閱讀OP。 'not exists'運算符應用於子查詢中的每一行,而不針對所查詢的行進行評估,僅在表中存在條件 - 這不是正確的用法。 – Mureinik 2015-02-11 06:34:15

+0

再次,不,:)你可以看到我不在乎伯爵。只有裸露存在或不存在** bold ** ANY ** bold **結果(r_id,u_id是主鍵,所以總有最大的ONE結果)。這正是「不存在」的原因。 – 2015-02-12 09:44:53