Menukar nilai kolom di MySQL


127

Saya memiliki tabel MySQL dengan koordinat, nama kolomnya adalah X dan Y. Sekarang saya ingin menukar nilai kolom dalam tabel ini, sehingga X menjadi Y dan Y menjadi X. Solusi yang paling jelas adalah mengganti nama kolom, tapi saya tidak ingin membuat perubahan struktur karena saya tidak selalu memiliki izin untuk melakukan itu.

Apakah ini bisa dilakukan dengan UPDATE dalam beberapa cara?Tabel PEMBARUAN SET X = Y, Y = X jelas tidak akan melakukan apa yang saya inginkan.


Sunting: Harap dicatat bahwa pembatasan saya pada izin, yang disebutkan di atas, secara efektif mencegah penggunaan ALTER TABLE atau perintah lain yang mengubah struktur tabel / database. Mengganti nama kolom atau menambahkan yang baru sayangnya bukan opsi.


5
sebagai catatan, UPDATE table SET X = Y, Y = Xadalah cara standar untuk melakukannya dalam SQL, hanya perilaku MySQL.
Antti Haapala

Jawaban:


204

Saya hanya harus berurusan dengan hal yang sama dan saya akan meringkas temuan saya.

  1. Itu UPDATE table SET X=Y, Y=X Pendekatan jelas tidak bekerja, karena hanya akan menetapkan kedua nilai untuk Y.

  2. Berikut adalah metode yang menggunakan variabel sementara. Terima kasih kepada Antony dari komentar http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ untuk tweak "IS NOT NULL". Tanpanya, kueri bekerja tanpa terduga. Lihat skema tabel di akhir posting. Metode ini tidak menukar nilai jika salah satunya adalah NULL. Gunakan metode # 3 yang tidak memiliki batasan ini.

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

  3. Metode ini ditawarkan oleh Dipin dalam, sekali lagi, komentar dari http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ . Saya pikir itu solusi yang paling elegan dan bersih. Ia bekerja dengan nilai NULL dan non-NULL.

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

  4. Pendekatan lain yang saya lakukan tampaknya berhasil:

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

Pada dasarnya, tabel 1 adalah yang diperbarui dan yang kedua digunakan untuk menarik data lama.
Perhatikan bahwa pendekatan ini membutuhkan kunci utama untuk hadir.

Ini adalah skema pengujian saya:

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

25
Seperti dicatat dalam dokumen MySQL, Tidak aman untuk menetapkan dan membaca variabel dalam satu pernyataan. Urutan operasi tidak dijamin. Jadi satu-satunya metode yang aman adalah # 4
AMIB

Opsi 4 bekerja untuk saya. Anda jelas dapat menambahkan lebih banyak kondisi ke klausa di mana jika Anda perlu menukar kolom hanya dengan beberapa baris.
Brad Campbell

7
Anda tahu, saya tidak pernah berpikir akan ada penggunaan praktis untuk pertanyaan wawancara bodoh yang meminta untuk menukar dua variabel tanpa menggunakan sementara, tapi ini dia, dan untuk bilangan bulat ini benar-benar berfungsi: perbarui swap_test set x = x + y, y = xy, x = xy;
izak

Sebagian besar dari jawaban ini adalah salinan / tempel langsung dari beerpla.net/2009/02/17/swapping-column-values-in-mysql

17
@Jhawins Itu karena beerpla.net adalah blog saya.
Artem Russakovskii

52

Anda bisa mengambil penjumlahan dan mengurangi nilai lawan menggunakan X dan Y

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

Ini adalah contoh uji (dan ini bekerja dengan angka negatif)

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>

Inilah swap yang dilakukan

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>

Cobalah !!!


5
Untuk angka itu memang yang paling rapi.
Akal Sehat Anda

Mungkin menjadi masalah jika suatu nilai meluap saat ditambahkan?
ToolmakerSteve

@ToolmakerSteve mungkin untuk TINYINTatau banyak sekali INT, Anda benar !!!
RolandoMySQLDBA

29

Kode berikut berfungsi untuk semua skenario dalam pengujian cepat saya:

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

UPDATE table swap_test? Bukankah seharusnya begitu UPDATE swap_test?
Pang

12

Tabel PEMBARUAN SET X = Y, Y = X akan melakukan persis apa yang Anda inginkan (edit: di PostgreSQL, bukan MySQL, lihat di bawah). Nilai diambil dari baris lama dan ditugaskan ke salinan baru dari baris yang sama, kemudian baris lama diganti. Anda tidak perlu menggunakan tabel sementara, kolom sementara, atau trik swap lainnya.

@ D4V360: Begitu. Itu mengejutkan dan tidak terduga. Saya menggunakan PostgreSQL dan jawaban saya berfungsi dengan benar di sana (saya sudah mencobanya). Lihat dokumen UPDATE PostgreSQL (di bawah Parameter, ekspresi), di mana disebutkan bahwa ekspresi di sisi kanan klausa SET secara eksplisit menggunakan nilai kolom yang lama. Saya melihat bahwa dokumen MySQL UPDATE yang sesuai berisi pernyataan "Penugasan UPDATE tabel tunggal umumnya dievaluasi dari kiri ke kanan" yang menyiratkan perilaku yang Anda uraikan.

Senang mendengarnya.


Terima kasih Greg dan D4V360, senang mengetahui perbedaan PostgreSQL dan MySQL tentang perilaku kueri pembaruan.
Vijay Dev

Pendekatan "x = y, y = x" juga berfungsi di Oracle, apa pun nilainya.
Burhan Ali

2
Saya menggunakan PostgreSQL dan SET X = Y, Y = X menyelamatkan saya :)
Anonim

4
IMHO jawaban ini berantakan - saran buruk dengan "oops never mind" ditambahkan. Setengah dari itu harus menjadi komentar dan satu-satunya bagian dari sisanya yang relevan dengan pertanyaan adalah tautan ke dokumen MySQL ...
Air

6

Ok, jadi hanya untuk bersenang-senang, Anda bisa melakukan ini! (dengan asumsi Anda menukar nilai string)

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)

Sedikit kesenangan menyalahgunakan proses evaluasi kiri-ke-kanan di MySQL.

Atau, gunakan XOR jika angka. Anda menyebutkan koordinat, jadi apakah Anda memiliki nilai integer yang indah, atau string kompleks?

Sunting: Hal-hal XOR bekerja seperti ini dengan cara:

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

5

Saya percaya memiliki variabel pertukaran perantara adalah praktik terbaik dengan cara seperti itu:

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

Pertama, selalu berhasil; kedua, ia bekerja terlepas dari tipe data.

Meskipun Keduanya

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

dan

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

bekerja biasanya, hanya untuk tipe data nomor by the way, dan itu adalah tanggung jawab Anda untuk mencegah overflow, Anda tidak dapat menggunakan XOR antara ditandatangani dan tidak ditandatangani, Anda juga tidak dapat menggunakan jumlah untuk kemungkinan melimpah.

Dan

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

tidak berfungsi jika c1 adalah 0 atau NULL atau nol string panjang atau hanya spasi.

Kita perlu mengubahnya

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

Ini skripnya:

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)

+1 untuk akhirnya menemukan penggunaan yang baik untuk pertanyaan wawancara bodoh di mana Anda harus menukar dua variabel tanpa sementara ;-)
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;
Sesuatu seperti ini?

Sunting: Tentang komentar Greg: Tidak, ini tidak berfungsi:

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)


Hanya untuk catatan: ini tidak bekerja di PostgreSQL sementara itu tidak bekerja di MySQL.
str

2

Ini pasti berhasil! Saya baru saja membutuhkannya untuk menukar kolom harga Euro dan SKK. :)

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

Di atas tidak akan berfungsi (ERROR 1064 (42000): Anda memiliki kesalahan dalam sintaks SQL Anda)


1

Dengan asumsi Anda telah menandatangani bilangan bulat di kolom Anda, Anda mungkin perlu menggunakan CAST (a ^ b AS SIGNED), karena hasil dari operator ^ adalah bilangan bulat 64-bit yang tidak ditandatangani di MySQL.

Jika itu membantu siapa pun, inilah metode yang saya gunakan untuk menukar kolom yang sama antara dua baris yang diberikan:

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

di mana $ 1 dan $ 2 adalah kunci dari dua baris dan $ 3 adalah hasil dari permintaan pertama.


1

Saya belum mencobanya tetapi

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

Mungkin melakukannya.

Menandai


1

Anda dapat mengubah nama kolom, tetapi ini lebih merupakan peretasan. Tapi hati-hati terhadap indeks apa pun yang ada di kolom ini


1

Nama tabel adalah pelanggan. bidang is a dan b, tukar nilai menjadi b ;.

PEMERIKSAAN pelanggan SET a = (@ temp: = a), a = b, b = @temp

Saya memeriksa ini berfungsi dengan baik.


1

Di SQL Server, Anda dapat menggunakan kueri ini:

update swaptable 
set col1 = t2.col2,
col2 = t2.col1
from swaptable t2
where id = t2.id

0

Swapping nilai kolom menggunakan kueri tunggal

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

Bersulang...!


1
Ini hanyalah pengulangan dari # 3 dari jawaban yang diterima .
Pang

0

Saya hanya perlu memindahkan nilai dari satu kolom ke yang lain (seperti pengarsipan) dan mengatur ulang nilai kolom asli.
Di bawah ini (referensi # 3 dari jawaban yang diterima di atas) bekerja untuk saya.

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

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

Contoh ini swap start_date dan END_DATE untuk catatan di mana tanggal adalah cara putaran yang salah (saat melakukan ETL ke menulis ulang utama, saya menemukan beberapa start tanggal lambat mereka akhir tanggal. Bawah, programmer buruk!).

Di situ, saya menggunakan MEDIUMINTs untuk alasan kinerja (seperti hari-hari Julian, tetapi memiliki root 0 dari 1900-01-01), jadi saya OK melakukan kondisi WHERE mdu.start_date> mdu.end_date .

PK berada di ketiga kolom secara individual (untuk alasan operasional / pengindeksan).

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;

FYI: Kode ini memperbarui 145 / 108.456 catatan dalam 0,203 detik. Itu adalah satu tugas dan jadi kinerja tidak kritis.
Andrew Foster

0

Katakanlah Anda ingin menukar nilai nama depan dan belakang di tb_user.

Yang paling aman adalah:

  1. Salin tb_user. Jadi Anda akan memiliki 2 tabel: tb_user dan tb_user_copy
  2. Gunakan permintaan UPDATE INNER JOIN
UPDATE tb_user a
INNER JOIN tb_user_copy b
ON a.id = b.id
SET a.first_name = b.last_name, a.last_name = b.first_name

0

Anda dapat menerapkan permintaan di bawah ini, Ini berfungsi sempurna untuk saya.

Table name: studentname
only single column available: name


update studentnames 
set names = case names 
when "Tanu" then "dipan"
when "dipan" then "Tanu"
end;

or

update studentnames 
set names = case names 
when "Tanu" then "dipan"
else "Tanu"
end;
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.