Domanda

Ho una tabella MySQL con coordinate, i nomi delle colonne sono X e Y.Ora voglio scambiare i valori delle colonne in questa tabella, in modo che X diventi Y e Y diventi X.La soluzione più evidente sarebbe rinominare le colonne, ma non voglio apportare modifiche alla struttura poiché non ho necessariamente le autorizzazioni per farlo.

È possibile farlo? AGGIORNAMENTO in qualche modo? AGGIORNA tabella SET X=Y, Y=X ovviamente non farò quello che voglio.


Modificare:Tieni presente che la mia limitazione sui permessi, menzionata sopra, impedisce di fatto l'uso di ALTER TABLE o altri comandi che modificano la struttura della tabella/database.Rinominare le colonne o aggiungerne di nuove purtroppo non sono opzioni.

È stato utile?

Soluzione

Ho dovuto affrontare lo stesso e riassumerò le mie scoperte.

  1. IL UPDATE table SET X=Y, Y=X l'approccio ovviamente non funziona, poiché imposterà semplicemente entrambi i valori su Y.

  2. Ecco un metodo che utilizza una variabile temporanea.Grazie ad Antony dai commenti di http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ per la modifica "IS NOT NULL".Senza di esso, la query funziona in modo imprevedibile.Vedi lo schema della tabella alla fine del post.Questo metodo non scambia i valori se uno di essi è NULL.Utilizza il metodo n. 3 che non presenta questa limitazione.

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

  3. Questo metodo è stato proposto da Dipin, ancora una volta, nei commenti di http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/.Penso che sia la soluzione più elegante e pulita.Funziona sia con valori NULL che non NULL.

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

  4. Un altro approccio che ho ideato sembra funzionare:

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

Essenzialmente, la prima tabella è quella che viene aggiornata e la seconda viene utilizzata per estrarre i vecchi dati.
Tieni presente che questo approccio richiede la presenza di una chiave primaria.

Questo è il mio schema di prova:

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

Altri suggerimenti

Potresti prendere la somma e sottrarre il valore opposto usando X e Y

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

Ecco un test di esempio (e funziona con numeri negativi)

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>

Ecco lo scambio in corso

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>

Provaci !!!

Il seguente codice funziona per tutti gli scenari nei miei test rapidi:

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

AGGIORNA tabella SET X=Y, Y=X farà esattamente quello che vuoi (modifica:in PostgreSQL, non MySQL, vedere sotto).I valori vengono presi dalla vecchia riga e assegnati a una nuova copia della stessa riga, quindi la vecchia riga viene sostituita.Non è necessario ricorrere all'utilizzo di una tabella temporanea, di una colonna temporanea o di altri trucchi di scambio.

@D4V360:Vedo.Questo è scioccante e inaspettato.Uso PostgreSQL e la mia risposta funziona correttamente lì (l'ho provato).Vedi il Documenti di aggiornamento PostgreSQL (sotto Parametri, espressione), dove menziona che le espressioni sul lato destro delle clausole SET utilizzano esplicitamente i vecchi valori delle colonne.Vedo che il corrispondente Documenti di aggiornamento MySQL contenere l'affermazione "Le assegnazioni UPDATE a tabella singola vengono generalmente valutate da sinistra a destra" che implica il comportamento che descrivi.

Buono a sapersi.

Ok, quindi, solo per divertimento, potresti farlo!(supponendo che tu stia scambiando valori di stringa)

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)

È un bel po' di divertimento abusare del processo di valutazione da sinistra a destra in MySQL.

In alternativa, usa semplicemente XOR se sono numeri.Hai menzionato le coordinate, quindi hai valori interi adorabili o stringhe complesse?

Modificare:A proposito, il materiale XOR funziona in questo modo:

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

Due alternative 1.Utilizzare una tabella temporanea 2.Indagare il Algoritmo XOR

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;
Qualcosa come questo?

Modificare:Riguardo al commento di Greg:No, questo non funziona:

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

mysql> aggiorna il set di test x=y, y=x;Query ok, 2 righe interessate (0,00 sec) righe abbinate:2 Modificato:2 Avvertenze:0

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

Credo che avere una variabile di scambio intermedia sia la migliore pratica in questo modo:

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

Innanzitutto, funziona sempre;in secondo luogo, funziona indipendentemente dal tipo di dati.

Nonostante entrambi

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

E

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

funzionano di solito, solo per il tipo di dati numerico tra l'altro, ed è tua responsabilità prevenire l'overflow, non puoi utilizzare XOR tra firmato e non firmato, inoltre non puoi utilizzare la somma per la possibilità di overflow.

E

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

non funziona se C1 è 0 o null o zero lunghezza o solo spazi.

Dobbiamo cambiarlo in

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

Ecco gli script:

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)

Questo funziona sicuramente!Ne avevo solo bisogno per scambiare le colonne dei prezzi in Euro e SKK.:)

UPDATE tbl SET X=Y, Y=@temp where @temp:=X;

Quanto sopra non funzionerà (ERRORE 1064 (42000):Hai un errore nella sintassi SQL)

Supponendo che tu abbia numeri interi con segno nelle tue colonne, potrebbe essere necessario utilizzare CAST(a ^ b AS SIGNED), poiché il risultato dell'operatore ^ è un numero intero a 64 bit senza segno in MySQL.

Nel caso possa aiutare qualcuno, ecco il metodo che ho usato per scambiare la stessa colonna tra due righe specificate:

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

dove $1 e $2 sono le chiavi di due righe e $3 è il risultato della prima query.

Non l'ho provato ma

UPDATE tbl SET @temp=X, X=Y, Y=@temp

Potrebbe farlo.

Segno

Voi Potevo cambiare i nomi delle colonne, ma questo è più un trucco.Ma fai attenzione agli indici che potrebbero trovarsi su queste colonne

Scambio di valori di colonna utilizzando una singola query

AGGIORNA mia_tabella SET a=@tmp:=a, a=b, b=@tmp;

saluti...!

Ho dovuto semplicemente spostare il valore da una colonna all'altra (come l'archiviazione) e reimpostare il valore della colonna originale.
Quanto segue (riferimento n. 3 dalla risposta accettata sopra) ha funzionato per me.

Update MyTable set X= (@temp:= X), X = 0, Y = @temp WHERE ID= 999;
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;

Questo esempio scambia data d'inizio E data di fine per i record in cui le date sono al contrario (durante l'esecuzione di ETL in una riscrittura importante, ne ho trovati alcuni inizio date successive alla loro FINE date.Giù, cattivi programmatori!).

In situ, sto usando MEDIUMINT per motivi di prestazioni (come i giorni giuliani, ma con una radice 0 di 1900-01-01), quindi stavo bene facendo una condizione di DOVE mdu.data_inizio > mdu.data_fine.

I PK erano su tutte e 3 le colonne individualmente (per ragioni operative/di indicizzazione).

UPDATE monitor_date mdu
INNER JOIN monitor_date mdc
    ON mdu.register_id = mdc.register_id
    AND mdu.start_date = mdc.start_date
    AND mdu.end_date = mdc.end_date
SET mdu.start_date = mdu.end_date, mdu.end_date = mdc.start_date
WHERE mdu.start_date > mdu.end_date;

Il nome della tabella è cliente. i campi sono a e b, scambia un valore con b;.

AGGIORNA SET cliente a=(@temp:=a), a = b, b = @temp

Ho controllato che funzioni bene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top