Permuter les valeurs de colonne indexées uniques dans la firebase database

J’ai une table de firebase database et l’un des champs (pas la clé primaire) contient un index unique. Maintenant, je veux échanger des valeurs sous cette colonne pour deux lignes. Comment cela pourrait-il être fait? Deux hacks que je connais sont:

  1. Supprimer les deux lignes et les réinsérer
  2. Mettez à jour les lignes avec d’autres valeurs et permutez, puis mettez à jour la valeur réelle.

Mais je ne veux pas aller pour ceux-ci car ils ne semblent pas être la solution appropriée au problème. Quelqu’un pourrait m’aider?

Je pense que vous devriez aller pour la solution 2. Il n’y a pas de fonction «swap» dans une variante SQL que je connais.

Si vous devez le faire régulièrement, je suggère une solution 1, en fonction de la manière dont les autres parties du logiciel utilisent ces données. Vous pouvez avoir des problèmes de locking si vous ne faites pas attention.

Mais en bref: il n’y a pas d’autre solution que celles que vous avez fournies.

Le mot magique est DEFERRABLE ici:

DROP TABLE ztable CASCADE; CREATE TABLE ztable ( id integer NOT NULL PRIMARY KEY , payload varchar ); INSERT INTO ztable(id,payload) VALUES (1,'one' ), (2,'two' ), (3,'three' ); SELECT * FROM ztable; -- This works, because there is no constraint UPDATE ztable t1 SET payload=t2.payload FROM ztable t2 WHERE t1.id IN (2,3) AND t2.id IN (2,3) AND t1.id <> t2.id ; SELECT * FROM ztable; ALTER TABLE ztable ADD CONSTRAINT OMG_WTF UNIQUE (payload) DEFERRABLE INITIALLY DEFERRED ; -- This should also work, because the constraint -- is deferred until "commit time" UPDATE ztable t1 SET payload=t2.payload FROM ztable t2 WHERE t1.id IN (2,3) AND t2.id IN (2,3) AND t1.id <> t2.id ; SELECT * FROM ztable; 

RÉSULTAT:

 DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ztable_pkey" for table "ztable" CREATE TABLE INSERT 0 3 id | payload ----+--------- 1 | one 2 | two 3 | three (3 rows) UPDATE 2 id | payload ----+--------- 1 | one 2 | three 3 | two (3 rows) NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "omg_wtf" for table "ztable" ALTER TABLE UPDATE 2 id | payload ----+--------- 1 | one 2 | two 3 | three (3 rows) 

Suite à la réponse d’Andy Irving

Cela a fonctionné pour moi (sur SQL Server 2005) dans une situation similaire où j’ai une clé composite et que je dois permuter un champ qui fait partie de la contrainte unique.

touche: pID, LNUM rec1: 10, 0 rec2: 10, 1 rec3: 10, 2

et je dois échanger LNUM de sorte que le résultat est

touche: pID, LNUM rec1: 10, 1 rec2: 10, 2 rec3: 10, 0

le SQL nécessaire:

 UPDATE DOCDATA SET LNUM = CASE LNUM WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END WHERE (pID = 10) AND (LNUM IN (0, 1, 2)) 

Il existe une autre approche qui fonctionne avec SQL Server: utilisez une table temporaire pour vous y joindre dans votre instruction UPDATE.

Le problème est dû au fait que deux lignes ont la même valeur en même temps , mais si vous mettez à jour les deux lignes simultanément (avec leurs nouvelles valeurs uniques), il n’y a pas de violation de contrainte.

Pseudo-code:

 -- setup initial data values: insert into data_table(id, name) values(1, 'A') insert into data_table(id, name) values(2, 'B') -- create temp table that matches live table select top 0 * into #tmp_data_table from data_table -- insert records to be swapped insert into #tmp_data_table(id, name) values(1, 'B') insert into #tmp_data_table(id, name) values(2, 'A') -- update both rows at once! No index violations! update data_table set name = #tmp_data_table.name from data_table join #tmp_data_table on (data_table.id = #tmp_data_table.id) 

Merci à Rich H pour cette technique. – Marque

Je pense aussi que le n ° 2 est le meilleur pari, bien que je sois sûr de l’emballer dans une transaction au cas où quelque chose irait mal au milieu de la mise à jour.

Une alternative (depuis que vous avez demandé) de mettre à jour les valeurs d’index unique avec des valeurs différentes serait de mettre à jour toutes les autres valeurs des lignes sur celles de l’autre ligne. Cela signifie que vous pouvez laisser les valeurs de l’index unique, et à la fin, vous vous retrouvez avec les données souhaitées. Attention cependant, si une autre table fait référence à cette table dans une relation de clé étrangère, toutes les relations de la firebase database restront intactes.

J’ai le même problème. Voici mon approche proposée dans PostgreSQL. Dans mon cas, mon index unique est une valeur de séquence, définissant un ordre d’utilisateur explicite sur mes lignes. L’utilisateur va mélanger les lignes dans une application Web, puis soumettre les modifications.

Je prévois d’append un déclencheur “avant”. Dans ce déclencheur, chaque fois que ma valeur d’index unique est mise à jour, je vérifierai si une autre ligne contient déjà ma nouvelle valeur. Si oui, je leur donnerai mon ancienne valeur et leur volerai la valeur.

J’espère que PostgreSQL me permettra de faire ce mélange dans le déclencheur avant.

Je posterai et je vous ferai savoir mon kilométrage.

En supposant que vous connaissiez le PK des deux lignes que vous souhaitez mettre à jour … Cela fonctionne dans SQL Server, ne peut pas parler pour d’autres produits. SQL est (censé être) atomique au niveau de l’instruction:

 CREATE TABLE testing ( cola int NOT NULL, colb CHAR(1) NOT NULL ); CREATE UNIQUE INDEX UIX_testing_a ON testing(colb); INSERT INTO testing VALUES (1, 'b'); INSERT INTO testing VALUES (2, 'a'); SELECT * FROM testing; UPDATE testing SET colb = CASE cola WHEN 1 THEN 'a' WHEN 2 THEN 'b' END WHERE cola IN (1,2); SELECT * FROM testing; 

alors vous partirez de:

 cola colb ------------ 1 b 2 a 

à:

 cola colb ------------ 1 a 2 b 

Oracle a différé le contrôle d’intégrité, ce qui résout exactement cela, mais il n’est pas disponible dans SQL Server ou MySQL.

Dans SQL Server, l’instruction MERGE peut mettre à jour des lignes qui interféreraient normalement avec une clé / index UNIQUE. (Juste testé cela parce que j’étais curieux.)

Cependant, vous devrez utiliser une table / variable temporaire pour fournir MERGE avec les lignes nécessaires.

Pour Oracle, il existe une option, DEFERRED, mais vous devez l’append à votre contrainte.

 SET CONSTRAINT emp_no_fk_par DEFERRED; 

Pour reporter TOUTES les contraintes qui peuvent être différées pendant toute la session, vous pouvez utiliser l’instruction ALTER SESSION SET contraintes = DEFERRED.

La source

Je pense généralement à une valeur que absolument aucun index dans ma table ne pourrait avoir. Généralement, pour les valeurs de colonne uniques, c’est très simple. Par exemple, pour les valeurs de la colonne ‘position’ (informations sur l’ordre de plusieurs éléments), c’est 0.

Vous pouvez ensuite copier la valeur A dans une variable, la mettre à jour avec la valeur B, puis définir la valeur B à partir de votre variable. Deux questions, je ne connais pas de meilleure solution.