2014-02-10 54 views
0

我有一個更新插入語句(http://www.the-art-of-web.com/sql/upsert/)執行INSERT每當有ID的行不存在,當行存在更新列:UPSERT和子選擇

WITH upsert AS 
    (UPDATE foo SET counter=counter+1 WHERE id='bar' RETURNING *) 
    INSERT INTO foo(id, counter) SELECT 'bar', 0 
WHERE NOT EXISTS (SELECT * FROM upsert) RETURNING counter; 

id是主鍵列(如預期)。直到這裏一切正常。

但有第三列'位置'可用於自定義排序。 如果有更新,我想保留當前值。

但insert語句需要額外的子查詢中使用不返回可能的最低位置:使用此聲明我得到一個錯誤

ERROR: duplicate key value violates unique constraint "id" 

什麼錯在這裏

WITH upsert AS 
    (UPDATE foo SET counter=counter+1 WHERE id='bar' RETURNING *) 
    INSERT INTO foo(id, counter, position) SELECT 'bar', 0, MIN(position)-1 from foo 
WHERE NOT EXISTS (SELECT * FROM upsert) RETURNING counter; 

+0

?你可以粘貼\ d foo –

+0

的輸出在附註中,你的upsert是錯誤的,並且不會做你認爲它的工作,除非你在運行它之前先鎖定TABLE foo IN EXCLUSIVE MODE。請參閱http://stackoverflow.com/q/17267417/398670及其鏈接。如果'id'有一個'unique'約束或'primary key',它不會插入重複項,但是當你一次從多個連接運行它時,由於競爭條件,它仍然會失敗,並出現重複鍵錯誤。 –

+0

@KouberSaparev:id被定義爲約束不爲null的整數默認nextval('foo_id_seq':: regclass) – lgersman

回答

2

的問題是,施加MIN()至0行返回一行(具有NULL值)

實施例:

test=> select min(1) where false; 
min 
----- 

(1 row) 

這不同於同一WHERE子句而不min()

test=> select 1 where false; 
?column? 
---------- 
(0 rows) 

所以當在子查詢中使用MIN()INSERT時,它會插入一個即使當WHERE子句評估爲false時也是新行,這會破壞此UPSERT的邏輯。

我認爲這是可以圍繞通過引入另一子查詢工作:

WITH upsert AS 
    (UPDATE foo SET counter=counter+1 WHERE id='bar' RETURNING *) 
    INSERT INTO foo(id, counter, position) 
    SELECT * FROM (SELECT 'bar', 0, MIN(position)-1 from foo) s 
      WHERE NOT EXISTS (SELECT * FROM upsert) 
    RETURNING counter; 

然而要注意惡補成一個單一的SQL語句時,這同時運行並沒有賦予系統的任何成功的保證。

中查看:約束 「ID」 是如何定義的
How do I do an UPSERT (MERGE, INSERT … ON DUPLICATE UPDATE) in PostgreSQL?