Pregunta

Tengo una tabla de base de datos y uno de los campos (no la clave principal) tiene un índice único.Ahora quiero intercambiar valores en esta columna por dos filas.¿Como se puede hacer esto?Dos trucos que conozco son:

  1. Elimine ambas filas y vuelva a insertarlas.
  2. Actualice filas con algún otro valor e intercambio y luego actualice al valor real.

Pero no quiero optar por estos porque no parecen ser la solución adecuada al problema.¿Podría alguien ayudarme?

¿Fue útil?

Solución

Creo que deberías optar por la solución 2.No existe ninguna función de 'intercambio' en ninguna variante de SQL que conozca.

Si necesita hacer esto con regularidad, le sugiero la solución 1, dependiendo de cómo otras partes del software utilicen estos datos.Puedes tener problemas de bloqueo si no tienes cuidado.

Pero en resumen:No hay otra solución que las que proporcionaste.

Otros consejos

La palabra mágica es APLAZABLE aquí:

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;

RESULTADO:

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)

Además de la respuesta de Andy Irving

Esto funcionó para mí (en SQL Server 2005) en una situación similar en la que tengo una llave compuesta y necesito cambiar un campo que forme parte de la restricción única.

llave:pid, lnum rec1:10, 0 Rec2:10, 1 rec3:10, 2

y necesito intercambiar LNUM para que el resultado sea

llave:pid, lnum rec1:10, 1 Rec2:10, 2 Rec3:10, 0

el SQL necesario:

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))

Existe otro enfoque que funciona con SQL Server:use una tabla temporal para unirse a ella en su declaración ACTUALIZAR.

El problema se produce por tener dos filas con el mismo valor. al mismo tiempo, pero si actualiza ambas filas a la vez (a sus valores nuevos y únicos), no se infringe ninguna restricción.

Pseudocódigo:

-- 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)

Gracias a Rich H por esta técnica.- Marca

También creo que el número 2 es la mejor opción, aunque me aseguraría de incluirlo en una transacción en caso de que algo salga mal a mitad de la actualización.

Una alternativa (ya que usted lo solicitó) para actualizar los valores del índice único con valores diferentes sería actualizar todos los demás valores en las filas a los de la otra fila.Hacer esto significa que puede dejar los valores del índice único en paz y, al final, terminará con los datos que desea.Sin embargo, tenga cuidado, en caso de que alguna otra tabla haga referencia a esta tabla en una relación de clave externa, todas las relaciones en la base de datos permanezcan intactas.

Tengo el mismo problema.Aquí está mi enfoque propuesto en PostgreSQL.En mi caso, mi índice único es un valor de secuencia que define un orden de usuario explícito en mis filas.El usuario barajará filas en una aplicación web y luego enviará los cambios.

Estoy planeando agregar un disparador "antes".En ese disparador, cada vez que se actualiza mi valor de índice único, miraré si alguna otra fila ya contiene mi nuevo valor.Si es así, les daré mi antiguo valor y efectivamente les robaré el valor.

Espero que PostgreSQL me permita hacer esta mezcla en el disparador anterior.

Volveré a publicar y te haré saber mi kilometraje.

Suponiendo que conoce la PK de las dos filas que desea actualizar...Esto funciona en SQL Server, no puedo hablar de otros productos.SQL es (se supone que es) atómico a nivel de declaración:

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;

entonces pasarás de:

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

a:

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

Para Oracle existe una opción, APLAZADA, pero debe agregarla a su restricción.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

Para aplazar TODAS las restricciones que se pueden aplazar durante toda la sesión, puede utilizar la instrucción ALTER SESSION SET constraints=DEFERRED.

Fuente

Oracle tiene una verificación de integridad diferida que resuelve exactamente esto, pero no está disponible ni en SQL Server ni en MySQL.

En SQL Server, la instrucción MERGE puede actualizar filas que normalmente romperían una CLAVE/ÍNDICE ÚNICO.(Acabo de probar esto porque tenía curiosidad).

Sin embargo, tendría que usar una tabla/variable temporal para proporcionar MERGE con las filas necesarias.

Normalmente pienso en un valor que ningún índice de mi tabla podría tener.Por lo general, para valores de columna únicos, es realmente fácil.Por ejemplo, para los valores de la columna 'posición' (información sobre el orden de varios elementos) es 0.

Luego puede copiar el valor A a una variable, actualizarlo con el valor B y luego establecer el valor B de su variable.Dos consultas, aunque no conozco una solución mejor.

1) cambiar los identificadores por nombre

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

Para la entrada de muestra, la salida es:

estudiante de identificación

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

"en caso de n número de filas, ¿cómo se gestionará...?"

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top