2017-04-23 254 views
0

我在兩個字段lane_id和position上有一個具有唯一索引的表。PostgreSQL:具有唯一索引的批量更新

現在,我想用這個查詢來更新行:如果我有一個以上的票

duplicate key value violates unique constraint "teams_ticket_position_bfcce9fa_uniq" 

UPDATE "teams_ticket" SET "position" = ("teams_ticket"."position" + 1) WHERE ("teams_ticket"."lane_id" = 1 AND "teams_ticket"."position" >= 0) 

與結束。

我該如何解決這個問題?

+0

取決於您的模式。這聽起來像是你對增加的值有一個唯一的鍵約束,這將是一個開始尋求修復的好地方。 – Makoto

+0

@Makoto是的,我知道。我有和索引(見票的第一行)。問題在於,將所有門票轉移一個位置應該以有效的情況結束(所有lane_id - 位置對仍然是唯一的)。但看起來他們一個接一個地移動,然後索引失敗。我想知道什麼是解決這個問題的正確方法。 – marxin

+1

這裏你需要'延遲'。 – wildplasser

回答

0

您需要deferrable約束條件(並且實際上暫時延遲它們)。可延遲意味着不會立即檢查約束(例如:當索引元組正在被重寫時),而是在事務(或語句)結束時,所有行已被更新:

- \ i tmp。 sql

CREATE TABLE positions 
     (seq SERIAL PRIMARY KEY 
     , position INTEGER NOT NULL UNIQUE DEFERRABLE 
     ); 

INSERT INTO positions(position) 
SELECT generate_series(1,10) gs; 

BEGIN; 
SET CONSTRAINTS ALL DEFERRED; 

update positions 
SET position= position +1 
WHERE seq <= 6 ; 

SELECT * FROM positions ; 

UPDATE positions SET position= position +1 
WHERE seq > 6 
     ; 

SELECT * FROM positions ; 

UPDATE positions SET position= position +1 
     ; 

SELECT * FROM positions ; 

COMMIT; 
+0

Downvoter:請解釋。 (因爲這是正確的答案,IMnsvHO) – wildplasser

+0

這是一個正確的解決方案。一般來說,失敗的原因是約束沒有被傳遞*。我在我的例子中所做的是: ALTER TABLE .. ADD CONSTRAINT ... DEFFERABLE INITIALLY DELERRED 以前我沒有最後一位,DEFFERABLE INITIALLY DEFERRED。 – marxin

-1

您可以嘗試

UPDATE teams_ticket t 
    SET position = q.position + 1 
    FROM (
    SELECT lane_id, position 
     FROM teams_ticket 
    WHERE lane_id = 1 
     AND position >= 0 
    ORDER BY lane_id, position DESC 
) q 
    WHERE t.lane_id = q.lane_id 
    AND t.position = q.position 
+0

這可能工作。或者它可能不會。子查詢中的ORDER BY本質上是無用的;優化器甚至可能會忽略它。 – wildplasser

0

您可以使用光標通過一個更新它們之一。

DO $$ 
    DECLARE r record; 
BEGIN 
    FOR r IN SELECT lane_id, position FROM teams_ticket WHERE lane_id=1 AND position >= 0 ORDER BY position DESC 
    LOOP 
    UPDATE teams_ticket SET position=position+1 WHERE position=r.position AND lane_id=r.lane_id; 
    END LOOP; 
END$$;