2017-04-23 2 views
0

J'ai une table avec un index unique sur deux champs, lane_id et position.PostgreSQL: mise à jour en masse avec index unique

Maintenant, je veux mettre à jour la ligne en utilisant cette requête:

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

qui se termine avec:

duplicate key value violates unique constraint "teams_ticket_position_bfcce9fa_uniq" 

Si j'ai plus d'un billet.

Comment puis-je résoudre ce problème?

+0

Dépend de votre schéma. Il semble que vous ayez une contrainte de clé unique sur une valeur que vous incrémentez, ce qui serait un bon point de départ pour commencer à chercher des solutions. – Makoto

+0

@Makoto Oui, je sais. J'ai et index (voir première ligne de billet). Le fait est que déplacer tous les tickets d'une position devrait aboutir à une situation valide (toutes les paires lane_id - position étant toujours uniques). Mais il semble qu'ils se sont déplacés un par un, puis l'index échoue. Je me demande quelle est la bonne façon de résoudre ce problème. – marxin

+1

Vous devez 'reportable' ici. – wildplasser

Répondre

0

Vous avez besoin de deferrable contraintes (et de les différer réellement, temporellement). Différable signifie que les contraintes ne sont pas vérifiées immédiatement (disons: lorsque l'index-tuple est réécrit) mais à la fin de la transaction (ou instruction), lorsque toutes les lignes ont été mises à jour:

- \ 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: veuillez expliquer. (puisque c'est la réponse correcte, IMnsvHO) – wildplasser

+0

Ceci est une solution correcte. En général, la raison d'un échec était que la contrainte n'était pas * deffered *. Ce que j'ai fait dans mon cas est: ALTER TABLE .. AJOUTER CONTRAINTE ... DÉFÉRABLE INITIALEMENT DIFFÉRÉ Auparavant, je n'avais pas le dernier bit, DÉFINISSABLE INITIALLY DIFFÉRÉ. – marxin

-1

Vous pouvez

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

Cela pourrait fonctionner. Ou peut-être pas. Le 'ORDER BY' dans la sous-requête est essentiellement inutile; l'optimiseur pourrait même l'ignorer. – wildplasser

0

Vous pouvez utiliser le curseur pour les mettre à jour un par un.

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$$;