2008-09-01 46 views
92

Tôi có bảng MySQL có tọa độ, tên cột là X và Y. Bây giờ tôi muốn trao đổi giá trị cột trong bảng này, để X trở thành Y và Y trở thành X. Giải pháp rõ ràng nhất sẽ đổi tên các cột, nhưng tôi không muốn thay đổi cấu trúc vì tôi không nhất thiết có quyền để thực hiện điều đó.Hoán đổi giá trị cột trong MySQL

Điều này có thể thực hiện với CẬP NHẬT theo một cách nào đó không? CẬP NHẬT bảng SET X = Y, Y = X rõ ràng sẽ không làm những gì tôi muốn.


Chỉnh sửa: Hãy ngăn chặn hiệu quả việc sử dụng ALTER TABLE hoặc các lệnh khác thay đổi cấu trúc bảng/cơ sở dữ liệu. Thay vào đó, việc đổi tên cột hoặc thêm cột mới không phải là tùy chọn.

+1

làm ghi chú, 'CẬP NHẬT bảng SET X = Y, Y = X' là cách tiêu chuẩn để thực hiện nó trong SQL, chỉ các lỗi của MySQL. –

Trả lời

135

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

  1. The UPDATE table SET X=Y, Y=X rõ ràng không hoạt động, vì nó sẽ chỉ cần đặt cả hai giá trị để Y.

  2. Dưới đây là một phương pháp sử dụng biến tạm thời. Nhờ Antony từ các ý kiến ​​của http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ cho tinh chỉnh "IS NOT NULL". Không có nó, truy vấn hoạt động không thể đoán trước. Xem lược đồ bảng ở cuối bài đăng. Phương thức này không trao đổi các giá trị nếu một trong số chúng là NULL. Sử dụng phương pháp # 3 không có giới hạn này.

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

  3. Phương pháp này được cung cấp bởi Dipin trong, nhưng một lần nữa, các ý kiến ​​của http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/. Tôi nghĩ đó là giải pháp thanh lịch và sạch sẽ nhất. Nó hoạt động với cả hai giá trị NULL và không NULL.

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

  4. Một cách tiếp cận tôi đã đưa ra với điều đó dường như làm việc:

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

Về cơ bản, bảng 1 là một trong những nhận được cập nhật và một trong 2 được sử dụng để kéo dữ liệu cũ từ.
Lưu ý rằng phương pháp này yêu cầu phải có khóa chính.

Đây là sơ đồ thử nghiệm của tôi:

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

Như đã lưu ý trong tài liệu MySQL, không an toàn để gán và đọc biến trong một câu lệnh đơn. Thứ tự của các hoạt động không được bảo đảm. Vì vậy, phương pháp an toàn duy nhất là # 4 – AMIB

+0

Tùy chọn 4 đã làm việc cho tôi. Bạn rõ ràng có thể thêm nhiều điều kiện hơn vào mệnh đề where nếu bạn cần trao đổi các cột chỉ cho một số hàng. –

+5

Bạn biết đấy, tôi không bao giờ nghĩ rằng có một cách sử dụng thực tế cho câu hỏi phỏng vấn ngu ngốc đó để trao đổi hai biến mà không sử dụng tạm thời, nhưng ở đây, và cho số nguyên này thực sự hoạt động: update swap_test set x = x + y , y = xy, x = xy; – izak

4

Hai lựa chọn thay thế 1. Sử dụng một bảng tạm thời 2. Điều tra cách tiếp cận 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

Chỉ dành cho bản ghi: * này * hoạt động trong PostgreSQL trong khi nó * không * hoạt động trong 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

Cảm ơn Greg và D4V360, tốt để biết sự khác biệt trong PostgreSQL và MySQL về hành vi của các truy vấn cập nhật. –

+0

Cách tiếp cận "x = y, y = x" cũng hoạt động trong Oracle, cho những gì nó có giá trị. –

+2

Tôi đã sử dụng PostgreSQL và SET X = Y, Y = X đã lưu tôi :) – 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

Các mã sau đây làm việc cho tất cả các kịch bản thử nghiệm nhanh của tôi:

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

'UPDATE table swap_test'? Không nên là 'UPDATE swap_test'? – Pang

1

Bạn thể thay đổi tên cột, nhưng đây là chi tiết của một hack.Nhưng hãy thận trọng của bất kỳ chỉ số mà bạn có thể vào các cột này

30

Bạn có thể mất số tiền và trừ đi giá trị đối lập sử dụng X và Y

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

Dưới đây là một thử nghiệm mẫu (và nó hoạt động với số âm)

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> 

Dưới đây là trao đổi được thực hiện

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> 

cho nó một thử! !!

+3

Đối với những con số nó thực sự là một trong những gọn gàng nhất. –

+0

@YourCommonSense Tiền thưởng của bạn nói rằng Ronaldo thực sự là Rolando. – Albzi

0

Swapping các giá trị cột sử dụng truy vấn duy nhất

CẬP NHẬT my_table SET a = @ tmp: = a, a = b, b = @ tmp;

cổ vũ ...!

+0

Đây chỉ là một lần lặp lại # 3 của [câu trả lời được chấp nhận] (http://stackoverflow.com/a/559291/1402846). – Pang

4

Tôi tin rằng có một biến đổi trung gian là thực hành tốt nhất theo cách như vậy:

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

Đầu tiên, nó hoạt động luôn; thứ hai, nó hoạt động bất kể kiểu dữ liệu.

Mặc dù of Both

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

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

đang làm việc thường, chỉ dành cho kiểu dữ liệu số bằng cách này, và nó là trách nhiệm của bạn để ngăn chặn tràn, bạn không thể sử dụng XOR giữa đã ký và chưa ký, bạn cũng không thể sử dụng tổng cho khả năng tràn.

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

không được làm việc nếu c1 là 0 hoặc NULL hoặc không có chiều dài chuỗi hoặc chỉ không gian.

Chúng ta cần thay đổi nó để

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

Đây là kịch bản:

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 cho cuối cùng tìm kiếm một sử dụng tốt cho các câu hỏi phỏng vấn ngu ngốc, nơi bạn phải trao đổi hai biến mà không có một tạm thời ;-) – 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

tôi phải chỉ di chuyển giá trị từ một cột để người kia (như lưu trữ) và đặt lại giá trị của cột ban đầu.
Dưới đây (tham chiếu số 3 từ câu trả lời được chấp nhận ở trên) đã làm việc cho tôi.

Update MyTable set X= (@temp:= X), X = 0, Y = @temp WHERE ID= 999; 
Các vấn đề liên quan