2009-01-05 17 views
24

Digamos que tengo una tabla tbl con columnas id y título. necesito cambiar todos los valores de la columna título:¿Es posible realizar múltiples actualizaciones con una sola instrucción UPDATE SQL?

  1. de 'a1' a 'a1',
  2. de 'a.1' a 'a1',
  3. de 'b-1 'a' b1 ',
  4. de' b.1 'a' b1 '.

En este momento, estoy realizando dos sentencias UPDATE:

UPDATE tbl SET title='a1' WHERE title IN ('a-1', 'a.1') 
UPDATE tbl SET title='b1' WHERE title IN ('b-1', 'b.1') 

Esto no es en absoluto un problema, si la tabla es pequeña, y la declaración única se completa en menos de un segundo y solo necesitas unas pocas sentencias para ejecutar.

Probablemente lo haya adivinado: tengo que encargarme de una gran tabla (una afirmación se completa en aproximadamente 90 segundos), y tengo un gran número de actualizaciones para realizar.

Entonces, ¿es posible combinar las actualizaciones para que solo analice la tabla una vez? O tal vez, hay una mejor manera de tratar en una situación como esta.

EDITAR: Tenga en cuenta que los datos reales con los que estoy trabajando y los cambios en los datos que tengo que realizar no son tan simples: las cadenas son más largas y no siguen ningún patrón (son datos de usuario) , por lo que no se pueden hacer suposiciones, puede ser cualquier cosa).

+0

Por lo tanto, inferir de su comentario EDITAR, las cadenas en sí pueden ser diferentes, pero la actualización que está intentando ¿SIGUE un patrón? Si es así, ¿qué es? Si nada tiene un patrón, entonces no hay solución, tiene que codificar cada actualización idiosincrásica individualmente –

+0

Tengo una lista de valores * correctos *, y tengo una lista claramente especificada de valores * incorrectos (y qué valor incorrecto debe ser cambiado a qué valor correcto). Entonces sí, las actualizaciones sí tienen un patrón. En resumen: cada actualización cambia UN valor, pero solo si el valor anterior está EN la lista de valores especificada. – Paulius

Respuesta

19

En un caso más general, donde puede haber muchos cientos de asignaciones a cada uno de los nuevos valores, que crearían una tabla separada de los valores antiguos y nuevos, y luego utilizar ese en la declaración de ACTUALIZACIÓN. En un dialecto de SQL:

CREATE TEMP TABLE mapper (old_val CHAR(5) NOT NULL, new_val CHAR(5) NOT NULL); 
...multiple inserts into mapper... 
INSERT INTO mapper(old_val, new_val) VALUES('a.1', 'a1'); 
INSERT INTO mapper(old_val, new_val) VALUES('a-1', 'a1'); 
INSERT INTO mapper(old_val, new_val) VALUES('b.1', 'b1'); 
INSERT INTO mapper(old_val, new_val) VALUES('b-1', 'b1'); 
...etcetera... 

UPDATE tbl 
    SET title = (SELECT new_val FROM mapper WHERE old_val = tbl.title) 
    WHERE title IN (SELECT old_val FROM mapper); 

Ambas sentencias de selección son cruciales. La primera es una subconsulta correlacionada (no necesariamente rápida, pero más rápida que la mayoría de las alternativas si la tabla mapeador tiene miles de filas) que extrae el nuevo valor de la tabla de asignación que corresponde al valor anterior.El segundo asegura que solo se modifiquen aquellas filas que tienen un valor en la tabla de mapeo; esto es crucial ya que de lo contrario, el título se establecerá en nulo para aquellas filas sin una entrada de mapeo (y esos fueron los registros que estaban bien antes de comenzar).

Para algunas alternativas, las operaciones CASE son correctas. Pero si tiene cientos o miles o millones de asignaciones para realizar, entonces es probable que exceda los límites de la longitud de la instrucción SQL en su DBMS.

+0

Esto es muy MUY interesante. Nunca pensé en eso. Las inserciones en un mapeador seguirán siendo rápidas, y la actualización solo escaneará mi tabla una vez y no es necesario que construya grandes consultas. – Paulius

+0

, pero es mejor usar una unión que una subconsulta correlacionada por motivos de rendimiento. – HLGEM

+0

@HLGEM: sí, si su DBMS admite la notación. ¿Le importaría ofrecer una sintaxis funcional para algunos DBMS que conozca? Si es así, edite mi respuesta: creo que tiene suficientes representantes para hacer eso. O avíseme por correo electrónico: vea mi página de perfil. –

3

Si las transformaciones son tan simples como sus ejemplos, se puede hacer la actualización con un poco de manipulación de cadenas:

UPDATE tbl 
SET title = left(title, 1) + right(title, 1) 
WHERE title IN ('a-1', 'a.1', 'b-1', 'b.1') 

¿Podría algo así como que el trabajo para usted?

+0

No, desafortunadamente, los datos reales que trato no son tan simples como en mi ejemplo. Esto no funcionaría para mí. Gracias de todos modos. – Paulius

+0

Suena como el uso de casperOne de la expresión CASE WHEN es el camino a seguir en ese momento. –

21

Puede utilizar una declaración y una serie de declaraciones de casos

update tbl 
    set title = 
    case 
     when title in ('a-1', 'a.1') then 'a1' 
     when title in ('b-1', 'b.1') then 'b1' 
     else title 
    end 

Por supuesto, esto provocará una escritura en cada disco, y con índices, que puede ser un problema, por lo que puede filtrar solamente las filas que desea cambiar:

update tbl 
    set title = 
    case 
     when title in ('a-1', 'a.1') then 'a1' 
     when title in ('b-1', 'b.1') then 'b1' 
     else title 
    end 
where 
    title in ('a.1', 'b.1', 'a-1', 'b-1') 

Eso reducirá el número de escrituras en la tabla.

0

O

Update Table set 
    title = Replace(Replace(title, '.', ''), '-', '') 
    Where title Like '[ab][.-]1' 
+0

Como mencioné en los comentarios de la respuesta de Matt, los datos no son tan simples en la base de datos real. – Paulius

+0

A riesgo de sonar obvio, bueno, entonces, la respuesta también será menos simple. ¿Cuál es el verdadero problema? –

+0

Tal vez, debería haber hecho los títulos diferentes en el ejemplo, para que no fueran tan simples. El verdadero problema es que los títulos reales son cadenas que no siguen ningún patrón; de hecho, son títulos generados por el usuario, por lo que no puedo hacer suposiciones sobre ellos. Edité mi pregunta también. – Paulius

8

Trabajando sin la respuesta de Jonathan.

UPDATE tbl 
    SET title = new_val 
FROM mapper 
WHERE title IN (SELECT old_val FROM mapper) 
    AND mapper.old_val = tbl.title; 

Su versión inicial requeriría una gran cantidad de lecturas en la tabla del asignador.

+0

@MrDenny: ¿puedo copiar su material en mi respuesta, con crédito dado, por supuesto? –

+0

¡Utilicé su consulta y funcionó como un amuleto! Estaba realmente sorprendido, fue realmente más rápido de lo que pensé. Muy agradable. – Paulius

Cuestiones relacionadas