2008-09-01 2 views
92

ich mit den Koordinaten einer MySQL-Tabelle haben, sind die Spaltennamen X und Y. Jetzt habe ich die Spaltenwerte in dieser Tabelle tauschen wollen, so dass X wird Y und Y wird X. Die offensichtlichste Lösung Ich würde die Spalten umbenennen, aber ich möchte keine Strukturänderungen vornehmen, da ich dazu nicht unbedingt berechtigt bin.Swapping Spaltenwerte in MySQL

Ist das möglich mit UPDATE in irgendeiner Weise zu tun? UPDATE Tabelle SET X = Y, Y = X wird offensichtlich nicht tun, was ich will.


Edit: Bitte beachten Sie, dass meine Beschränkung auf Berechtigungen, wie oben erwähnt, kann die Verwendung von ALTEN TABLE oder anderen Befehlen verhindert, die die Tabelle/Datenbankstruktur ändern. Das Umbenennen von Spalten oder das Hinzufügen neuer Spalten sind leider keine Optionen.

+1

als eine Notiz, 'UPDATE-Tabelle SET X = Y, Y = X 'ist die Standardmethode, es in SQL zu tun, nur MySQL verhält sich falsch. –

Antwort

135

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

  1. The UPDATE table SET X=Y, Y=X Ansatz untersuchen, da es nur die beiden Werte gesetzt werden

  2. Hier auf Y. a ist Methode, die eine temporäre Variable verwendet. Danke an Antony aus den Kommentaren von http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ für den "IS NOT NULL" Tweak. Ohne sie funktioniert die Abfrage unvorhersehbar. Siehe das Tabellenschema am Ende des Posts. Diese Methode tauscht die Werte nicht aus, wenn einer von ihnen NULL ist. Verwenden Sie Methode 3, die diese Einschränkung nicht aufweist.

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

  3. Diese Methode wurde von Dipin angeboten in, wieder einmal, die Kommentare von http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/. Ich denke, es ist die eleganteste und sauberste Lösung. Es funktioniert mit NULL- und Nicht-NULL-Werten.

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

  4. Ein weiterer Ansatz, den ich mit, dass kam scheint zu funktionieren:

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

Im Wesentlichen ist die erste Tabelle die eine immer aktualisiert, und die zweite man verwendet ziehe die alten Daten aus.
Beachten Sie, dass für diesen Ansatz ein Primärschlüssel erforderlich ist.

Dies ist mein Test-Schema:

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

Wie in MySQL-Dokumenten erwähnt, ist es nicht sicher, Variablen in einer einzigen Anweisung zuzuordnen und zu lesen. Die Reihenfolge der Vorgänge ist nicht garantiert. So ist die einzige sichere Methode # 4 – AMIB

+0

Option 4 arbeitete für mich. Sie können natürlich der where-Klausel weitere Bedingungen hinzufügen, wenn Sie die Spalten nur für einige Zeilen austauschen müssen. –

+5

Weißt du, ich hätte nie gedacht, dass es eine praktische Verwendung für diese dumme Interviewfrage geben würde, die zwei Variablen ohne ein temporäres austauschen möchte, aber hier ist es, und für ganze Zahlen würde das auch funktionieren: update swap_test set x = x + y , y = xy, x = xy; – izak

4

Zwei Alternativen 1. Verwenden Sie eine temporäre Tabelle 2. die offensichtlich nicht funktioniert, XOR algorithm

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

Nur für das Protokoll: Das * funktioniert * in PostgreSQL, während es * nicht * in MySQL funktioniert. – 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

Dank Greg und D4V360, gut zu wissen, die Unterschiede in PostgreSQL und MySQL über das Verhalten der Update-Anfragen. –

+0

Der Ansatz "x = y, y = x" funktioniert auch in Oracle, wofür es sich lohnt. –

+2

Ich habe PostgreSQL und SET X = Y, Y = X gespeichert mich :) – 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

Der folgende Code für alle Szenarien in meinem schnellen Test funktioniert:

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

'UPDATE table swap_test'? Sollte es nicht "UPDATE swap_test" sein? – Pang

1

Sie konnte Spalte ändern Namen, aber das ist eher ein hacken.Aber seien Sie vorsichtig von Indizes, die für diesen Spalten sein kann

30

Sie die Summe nehmen könnte und subtrahieren den gegnerischen Wert unter Verwendung von X- und Y-

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

Hier ist ein Beispiel-Test (und es funktioniert mit negativen Zahlen)

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> 

Hier ist der Swap-

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> 

versuchen sie es mal durchgeführt wird! !!

+3

Für die Zahlen ist es in der Tat ein ordentliches. –

+0

@YourCommonSense Ihr Kopfgeld sagt Ronaldo, wenn es in der Tat Rolando ist. – Albzi

0

Swapping von -Spaltenwerten mit einzelner Abfrage

UPDATE my_table SET a = @ tmp: = a, a = b, b = @ tmp;

Prost ...!

+0

Dies ist nur eine Wiederholung von # 3 von [die akzeptierte Antwort] (http://stackoverflow.com/a/559291/1402846). – Pang

4

Ich habe glauben, dass ein Zwischen Austausch Variable die beste Praxis in einer solchen Art und Weise ist:

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

Erstens, es funktioniert immer; Zweitens funktioniert es unabhängig vom Datentyp.

Trotz beider

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

und

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

arbeiten in der Regel nur für Zahlendatentyp von der Art und Weise, und es liegt in Ihrer Verantwortung Überlauf zu verhindern, können Sie nicht XOR verwenden, um zwischen signiert und unsigniert, Sie können die Summe auch nicht für überlaufende Möglichkeiten verwenden.

Und

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

funktioniert nicht wenn c1 0 oder NULL oder leeren String oder nur Leerzeichen.

Wir brauchen es

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

Hier ist die Skripte zu

ändern:

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 für endlich eine gute Verwendung für die dumme Interviewfrage zu finden, wo man zwei Variablen ohne eine temporäre austauschen muss ;-) – 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

ich von einer Spalte nur Wert musste zum anderen bewegen (wie Archivierung) und Zurücksetzen der Wert der ursprünglichen Spalte.
Die unten (Referenz von # 3 aus akzeptierten Antwort oben) funktionierte für mich.