2008-09-01 19 views
92

Tengo una tabla MySQL con coordenadas, los nombres de columna son X e Y. Ahora quiero intercambiar los valores de columna en esta tabla, de modo que X se convierta en Y e Y se convierta en X. La solución más aparente estaría renombrando las columnas, pero no quiero hacer cambios de estructura ya que no necesariamente tengo permisos para hacerlo.Intercambiando valores de columna en MySQL

¿Esto es posible con ACTUALIZAR de alguna manera? tabla de ACTUALIZACIÓN SET X = Y, Y = X obviamente no hará lo que quiero.


Editar: Tenga en cuenta que mi restricción de permisos, mencionado anteriormente, impide eficazmente el uso de ALTER TABLE o otros comandos que cambian la estructura de la tabla/base de datos. Cambiar el nombre de las columnas o agregar nuevas, desafortunadamente no son opciones.

+1

como una nota, 'tabla de ACTUALIZACIÓN SET X = Y, Y = X' es la forma estándar de hacerlo en SQL, solo MySQL se comporta mal. –

Respuesta

135

I just had to deal with the same and I'll summarize my findings.

  1. The UPDATE table SET X=Y, Y=X, obviamente, no funciona, ya que sólo va establezca ambos valores a Y.

  2. Aquí hay una método que usa una variable temporal. Gracias a Antony por los comentarios de http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ para el ajuste "IS NOT NULL". Sin él, la consulta funciona de manera impredecible. Vea el esquema de la tabla al final de la publicación. Este método no intercambia los valores si uno de ellos es NULL. Use el método n. ° 3 que no tenga esta limitación.

    UPDATE swap_test SET x=y, [email protected] WHERE (@temp:=x) IS NOT NULL;

  3. Este método fue ofrecida por Dipin en, una vez más, los comentarios de http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/. Creo que es la solución más elegante y limpia. Funciona con valores NULL y no NULL.

    UPDATE swap_test SET x=(@temp:=x), x = y, y = @temp;

  4. Otro enfoque que se me ocurrió que parece funcionar:

    UPDATE swap_test s1, swap_test s2 SET s1.x=s1.y, s1.y=s2.x WHERE s1.id=s2.id;

En esencia, la primera tabla es la que recibir actualización y el segundo uno está acostumbrado a extraer los datos antiguos de.
Tenga en cuenta que este enfoque requiere la presencia de una clave principal.

Esta es mi esquema de prueba:

CREATE TABLE `swap_test` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `x` varchar(255) DEFAULT NULL, 
    `y` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB; 

INSERT INTO `swap_test` VALUES ('1', 'a', '10'); 
INSERT INTO `swap_test` VALUES ('2', NULL, '20'); 
INSERT INTO `swap_test` VALUES ('3', 'c', NULL); 
+15

Como se señala en los documentos de MySQL, no es seguro asignar y leer variables en una sola declaración. El orden de las operaciones no está garantizado. Así que el único método seguro es # 4 – AMIB

+0

La opción 4 funcionó para mí. Obviamente, puede agregar más condiciones a la cláusula where si necesita intercambiar las columnas solo para algunas filas. –

+5

Sabes, nunca pensé que hubiera un uso práctico para esa estúpida pregunta de entrevista que pedía intercambiar dos variables sin usar una temporal, pero aquí está, y para enteros esto realmente funcionaría: actualizar swap_test establecer x = x + y , y = xy, x = xy; – izak

4

 
ALTER TABLE table ADD COLUMN tmp; 
UPDATE table SET tmp = X; 
UPDATE table SET X = Y; 
UPDATE table SET Y = tmp; 
ALTER TABLE table DROP COLUMN tmp; 
Something like this?

Edit: About Greg's comment: No, this doesn't work:

 
mysql> select * from test; 
+------+------+ 
| x | y | 
+------+------+ 
| 1 | 2 | 
| 3 | 4 | 
+------+------+ 
2 rows in set (0.00 sec)

mysql> update test set x=y, y=x; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warnings: 0

mysql> select * from test; +------+------+ | x | y | +------+------+ | 2 | 2 | | 4 | 4 | +------+------+ 2 rows in set (0.00 sec)

+0

Solo para el registro: Este * does * funciona en PostgreSQL mientras que * no * funciona en MySQL. – str

8

UPDATE table SET X=Y, Y=X will do precisely what you want (edit: in PostgreSQL, not MySQL, see below). The values are taken from the old row and assigned to a new copy of the same row, then the old row is replaced. You do not have to resort to using a temporary table, a temporary column, or other swap tricks.

@D4V360: I see. That is shocking and unexpected. I use PostgreSQL and my answer works correctly there (I tried it). See the PostgreSQL UPDATE docs (under Parameters, expression), where it mentions that expressions on the right hand side of SET clauses explicitly use the old values of columns. I see that the corresponding MySQL UPDATE docs contain the statement "Single-table UPDATE assignments are generally evaluated from left to right" which implies the behaviour you describe.

Good to know.

+0

Gracias Greg y D4V360, es bueno saber las diferencias en PostgreSQL y MySQL sobre el comportamiento de las consultas de actualización. –

+0

El enfoque "x = y, y = x" también funciona en Oracle, por lo que vale. –

+2

Utilicé PostgreSQL y SET X = Y, Y = X me salvó :) – Anonymous

5

Ok, so just for fun, you could do this! (assuming you're swapping string values)

mysql> select * from swapper; 
+------+------+ 
| foo | bar | 
+------+------+ 
| 6 | 1 | 
| 5 | 2 | 
| 4 | 3 | 
+------+------+ 
3 rows in set (0.00 sec) 

mysql> update swapper set 
    -> foo = concat(foo, "###", bar), 
    -> bar = replace(foo, concat("###", bar), ""), 
    -> foo = replace(foo, concat(bar, "###"), ""); 

Query OK, 3 rows affected (0.00 sec) 
Rows matched: 3 Changed: 3 Warnings: 0 

mysql> select * from swapper; 
+------+------+ 
| foo | bar | 
+------+------+ 
| 1 | 6 | 
| 2 | 5 | 
| 3 | 4 | 
+------+------+ 
3 rows in set (0.00 sec) 

A nice bit of fun abusing the left-to-right evaluation process in MySQL.

Alternatively, just use XOR if they're numbers. You mentioned coordinates, so do you have lovely integer values, or complex strings?

Edit: The XOR stuff works like this by the way:

update swapper set foo = foo^bar, bar = foo^bar, foo = foo^bar; 
1

Assuming you have signed integers in your columns, you may need to use CAST(a^b AS SIGNED), since the result of the^operator is an unsigned 64-bit integer in MySQL.

In case it helps anyone, here's the method I used to swap the same column between two given rows:

SELECT BIT_XOR(foo) FROM table WHERE key = $1 OR key = $2 

UPDATE table SET foo = CAST(foo^$3 AS SIGNED) WHERE key = $1 OR key = $2 

where $1 and $2 are the keys of two rows and $3 is the result of the first query.

2

This surely works! I've just needed it to swap Euro and SKK price columns. :)

UPDATE tbl SET X=Y, [email protected] where @temp:=X; 

The above will not work (ERROR 1064 (42000): You have an error in your SQL syntax)

18

El código siguiente funciona para todos los escenarios en mi prueba rápida:

UPDATE table swap_test 
    SET x=(@temp:=x), x = y, y = @temp 
+0

'UPDATE tabla swap_test'? ¿No debería ser 'UPDATE swap_test'? – Pang

1

Usted podría nombres de columna cambio, pero esto es más de un cortar.Pero tenga cuidado de los índices que pueden estar en estas columnas

30

usted podría tomar la suma y resta el valor de oposición usando X e Y

UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y; 

Aquí es una prueba de muestra (y funciona con números negativos)

mysql> use test 
Database changed 
mysql> drop table if exists swaptest; 
Query OK, 0 rows affected (0.03 sec) 

mysql> create table swaptest (X int,Y int); 
Query OK, 0 rows affected (0.12 sec) 

mysql> INSERT INTO swaptest VALUES (1,2),(3,4),(-5,-8),(-13,27); 
Query OK, 4 rows affected (0.08 sec) 
Records: 4 Duplicates: 0 Warnings: 0 

mysql> SELECT * FROM swaptest; 
+------+------+ 
| X | Y | 
+------+------+ 
| 1 | 2 | 
| 3 | 4 | 
| -5 | -8 | 
| -13 | 27 | 
+------+------+ 
4 rows in set (0.00 sec) 

mysql> 

Aquí es el canje se realiza

mysql> UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y; 
Query OK, 4 rows affected (0.07 sec) 
Rows matched: 4 Changed: 4 Warnings: 0 

mysql> SELECT * FROM swaptest; 
+------+------+ 
| X | Y | 
+------+------+ 
| 2 | 1 | 
| 4 | 3 | 
| -8 | -5 | 
| 27 | -13 | 
+------+------+ 
4 rows in set (0.00 sec) 

mysql> 

darle una oportunidad! !!

+3

Para los números es de hecho el más adecuado. –

+0

@YourCommonSense Su recompensa dice Ronaldo cuando en realidad es Rolando. – Albzi

0

Intercambio de valores de columna usando sola consulta

ACTUALIZACIÓN my_table SET a = @ tmp: = a, a = b, b = @ tmp;

aplausos ...!

+0

Esto es solo una repetición del # 3 de [la respuesta aceptada] (http://stackoverflow.com/a/559291/1402846). – Pang

4

creo que tiene una variable central intermedia es la mejor práctica de tal manera:

update z set c1 = @c := c1, c1 = c2, c2 = @c 

En primer lugar, funciona siempre; segundo, funciona independientemente del tipo de datos.

A pesar de dos

update z set c1 = c1^c2, c2 = c1^c2, c1 = c1^c2 

y

update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2 

están trabajando normalmente, sólo por el número de tipo de datos, por cierto, y es su responsabilidad para evitar desbordamiento, no se puede utilizar XOR entre firmado y sin firmar, tampoco puede usar la suma por posibilidad de desbordamiento.

Y

update z set c1 = c2, c2 = @c where @c := c1 

no está funcionando si c1 es 0 o NULL o cero longitud de la cadena o sólo espacios.

Necesitamos cambiarlo a

update z set c1 = c2, c2 = @c where if((@c := c1), true, true) 

Aquí es las secuencias de comandos:

mysql> create table z (c1 int, c2 int) 
    -> ; 
Query OK, 0 rows affected (0.02 sec) 

mysql> insert into z values(0, 1), (-1, 1), (pow(2, 31) - 1, pow(2, 31) - 2) 
    -> ; 
Query OK, 3 rows affected (0.00 sec) 
Records: 3 Duplicates: 0 Warnings: 0 

mysql> select * from z; 
+------------+------------+ 
| c1   | c2   | 
+------------+------------+ 
|   0 |   1 | 
|   -1 |   1 | 
| 2147483647 | 2147483646 | 
+------------+------------+ 
3 rows in set (0.02 sec) 

mysql> update z set c1 = c1^c2, c2 = c1^c2, c1 = c1^c2; 
ERROR 1264 (22003): Out of range value for column 'c1' at row 2 
mysql> update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2; 
ERROR 1264 (22003): Out of range value for column 'c1' at row 3 

mysql> select * from z; 
+------------+------------+ 
| c1   | c2   | 
+------------+------------+ 
|   0 |   1 | 
|   1 |   -1 | 
| 2147483646 | 2147483647 | 
+------------+------------+ 
3 rows in set (0.02 sec) 

mysql> update z set c1 = c2, c2 = @c where @c := c1; 
Query OK, 2 rows affected (0.00 sec) 
Rows matched: 2 Changed: 2 Warnings: 0 

mysql> select * from z; 
+------------+------------+ 
| c1   | c2   | 
+------------+------------+ 
|   0 |   1 | 
|   -1 |   1 | 
| 2147483647 | 2147483646 | 
+------------+------------+ 
3 rows in set (0.00 sec) 

mysql> select * from z; 
+------------+------------+ 
| c1   | c2   | 
+------------+------------+ 
|   1 |   0 | 
|   1 |   -1 | 
| 2147483646 | 2147483647 | 
+------------+------------+ 
3 rows in set (0.00 sec) 

mysql> update z set c1 = @c := c1, c1 = c2, c2 = @c; 
Query OK, 3 rows affected (0.02 sec) 
Rows matched: 3 Changed: 3 Warnings: 0 

mysql> select * from z; 
+------------+------------+ 
| c1   | c2   | 
+------------+------------+ 
|   0 |   1 | 
|   -1 |   1 | 
| 2147483647 | 2147483646 | 
+------------+------------+ 
3 rows in set (0.00 sec) 

mysql>update z set c1 = c2, c2 = @c where if((@c := c1), true, true); 
Query OK, 3 rows affected (0.02 sec) 
Rows matched: 3 Changed: 3 Warnings: 0 

mysql> select * from z; 
+------------+------------+ 
| c1   | c2   | 
+------------+------------+ 
|   1 |   0 | 
|   1 |   -1 | 
| 2147483646 | 2147483647 | 
+------------+------------+ 
3 rows in set (0.00 sec) 
+0

+1 para finalmente encontrar un buen uso para la estúpida pregunta de la entrevista donde tienes que intercambiar dos variables sin una temporal ;-) – izak

0
CREATE TABLE Names 
(
F_NAME VARCHAR(22), 
L_NAME VARCHAR(22) 
); 

INSERT INTO Names VALUES('Ashutosh', 'Singh'),('Anshuman','Singh'),('Manu', 'Singh'); 

UPDATE Names N1 , Names N2 SET N1.F_NAME = N2.L_NAME , N1.L_NAME = N2.F_NAME 
WHERE N1.F_NAME = N2.F_NAME; 

SELECT * FROM Names; 
0

que tenía que basta con mover el valor de una columna a la otra (como el archivado) y restablecer la valor de la columna original.
Lo siguiente (referencia del n. ° 3 de la respuesta aceptada arriba) funcionó para mí.

Update MyTable set X= (@temp:= X), X = 0, Y = @temp WHERE ID= 999; 
Cuestiones relacionadas