MySQL에서 열 값 교환


127

좌표가있는 MySQL 테이블이 있고 열 이름은 X와 Y입니다. 이제 X에서 Y가되고 Y가 X가되도록이 테이블의 열 값을 바꾸고 싶습니다. 가장 확실한 해결책은 열의 이름을 바꾸는 것이지만 내가 할 수있는 권한이 없기 때문에 구조를 변경하고 싶지 않습니다.

이것은 어떤 방식 으로 UPDATE 와 관련이 있습니까? 업데이트 테이블 SET X = Y, Y = X는 분명히 내가 원하는 것을하지 않습니다.


편집 : 위에서 언급 한 권한에 대한 제한으로 인해 ALTER TABLE 또는 테이블 / 데이터베이스 구조를 변경하는 다른 명령을 효과적으로 사용할 수 없습니다. 열 이름을 바꾸거나 새 열을 추가하는 것은 불행히도 옵션이 아닙니다.


5
참고 UPDATE table SET X = Y, Y = X로 SQL에서이를 수행하는 표준 방법이며 MySQL 만 잘못 작동합니다.
Antti Haapala

답변:


204

방금 똑같은 문제를 해결해야했으며 내 연구 결과를 요약하겠습니다.

  1. UPDATE table SET X=Y, Y=X방법은 두 값을 모두 Y로 설정하기 때문에 분명히 작동하지 않습니다.

  2. 임시 변수를 사용하는 방법은 다음과 같습니다. "IS NOT NULL"조정에 대한 http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ 의 의견에서 Antony에게 감사드립니다 . 이것이 없으면 쿼리가 예기치 않게 작동합니다. 게시물 끝에있는 테이블 스키마를 참조하십시오. 이 방법은 값 중 하나가 NULL 인 경우 값을 교환하지 않습니다. 이 제한이없는 방법 # 3을 사용하십시오.

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

  3. 이 방법은 Dipin에서 http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/에 대한 의견으로 제공되었습니다 . 가장 우아하고 깨끗한 솔루션이라고 생각합니다. NULL 값과 NULL이 아닌 값 모두에서 작동합니다.

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

  4. 내가 생각해 낸 또 다른 접근법은 효과가있는 것 같습니다.

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

기본적으로 첫 번째 테이블은 업데이트되는 테이블이고 두 ​​번째 테이블은 이전 데이터를 가져 오는 데 사용됩니다.
이 방법을 사용하려면 기본 키가 있어야합니다.

이것은 내 테스트 스키마입니다.

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
MySQL 문서에서 언급했듯이 단일 명령문으로 변수를 할당하고 읽는 것은 안전하지 않습니다. 작동 순서는 보장되지 않습니다. 따라서 유일한 안전한 방법은 # 4
AMIB

옵션 4가 나를 위해 일했습니다. 일부 행에 대해서만 열을 교체해야하는 경우 where 절에 더 많은 조건을 추가 할 수 있습니다.
Brad Campbell

7
아시다시피, 임시 인터뷰를 사용하지 않고 두 변수를 바꾸라고 요구하는 어리석은 인터뷰 질문에는 실용적이 없다고 생각했지만 여기에 있습니다. 정수는 실제로 작동합니다 : update swap_test set x = x + y, y = xy, x = xy;
izak


17
@Jhawins beerpla.net이 내 블로그이기 때문입니다.
Artem Russakovskii

52

X와 Y를 사용하여 합을 취하고 반대 값을 뺄 수 있습니다.

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

다음은 샘플 테스트입니다 (음수와 함께 작동).

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>

스왑이 수행되고 있습니다.

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>

시도 해봐 !!!


5
숫자의 경우 실제로 가장 작은 숫자입니다.
당신의 상식

추가 할 때 값이 오버플로되면 문제가 될 수 있습니까?
ToolmakerSteve

@ToolmakerSteve 아마도 TINYINT또는 거대한 가치가 INT있습니다. 맞습니다!
RolandoMySQLDBA

29

다음 코드는 빠른 테스트의 모든 시나리오에서 작동합니다.

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

UPDATE table swap_test? 그렇지 UPDATE swap_test않습니까?
Pang

12

업데이트 테이블 SET X = Y, Y = X 는 원하는대로 정확하게 수행합니다 (편집 : MySQL이 아닌 PostgreSQL에서 아래 참조). 이전 행에서 값을 가져와 같은 행의 새 사본에 할당 한 다음 이전 행을 바꿉니다. 임시 테이블, 임시 열 또는 다른 스왑 트릭을 사용하지 않아도됩니다.

@ D4V360 : 알겠습니다. 충격적이고 예상치 못한 일입니다. PostgreSQL을 사용하고 답변이 올바르게 작동합니다 (시도했습니다). SET 절의 오른쪽에있는 표현식이 명시 적으로 이전 열 값을 사용한다고 언급하는 PostgreSQL UPDATE 문서 (매개 변수, 표현식 아래)를 참조하십시오 . 해당 MySQL UPDATE 문서가 "단일 테이블 UPDATE 할당이 일반적으로 왼쪽에서 오른쪽으로 평가됩니다"라는 문장이 포함되어 .

알아 둘만 한.


업데이트 쿼리의 동작에 대한 PostgreSQL과 MySQL의 차이점을 알고있는 Greg와 D4V360에게 감사합니다.
Vijay Dev

"x = y, y = x"접근 방식은 Oracle에서도 가치가 있습니다.
Burhan Ali

2
PostgreSQL을 사용했고 SET X = Y, Y = X가 저를 구했습니다 :)
Anonymous

4
IMHO이 답변은 엉망입니다- "oops never mind"가 추가 된 나쁜 조언입니다. 그것의 절반은 의견이어야하고 질문과 관련된 나머지 부분의 유일한 부분은 MySQL 문서와의 링크입니다.
Air

6

좋아, 그냥 재미로, 당신은 이것을 할 수 있습니다! (문자열 값을 교환한다고 가정)

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)

MySQL에서 왼쪽에서 오른쪽으로 평가 프로세스를 남용하는 것은 재미있다.

또는 숫자 인 경우 XOR을 사용하십시오. 좌표를 언급 했으므로 멋진 정수 값이나 복잡한 문자열이 있습니까?

편집 : XOR 물건은 다음과 같이 작동합니다.

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

5

다음과 같은 방법으로 중간 교환 변수가 가장 좋습니다.

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

첫째, 항상 작동합니다. 둘째, 데이터 유형에 관계없이 작동합니다.

둘 다에도 불구하고

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

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

일반적으로 숫자 데이터 형식에 대해서만 작동하며 오버플로를 방지하는 것은 사용자의 책임입니다. 부호가있는 것과 부호가없는 사이에 XOR을 사용할 수 없으며 오버플로 가능성에 대해 합계를 사용할 수 없습니다.

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

c1이 0 또는 NULL이거나 길이가 0 인 문자열이거나 공백 일 경우 작동하지 않습니다.

우리는 그것을 바꿔야한다

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

스크립트는 다음과 같습니다.

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 ;-)
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;
이 같은?

편집 : 그렉의 의견에 관하여 : 아니오, 작동하지 않습니다 :

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)


그냥 레코드 : 이것은 하지 가 않습니다 동안 PostgreSQL의에서 작업을 하지 MySQL의에서 작동합니다.
str

2

이것은 확실히 작동합니다! 나는 유로와 SKK 가격 열을 교환하기 위해 방금 필요했습니다. :)

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

위의 방법은 작동하지 않습니다 (오류 1064 (42000) : SQL 구문에 오류가 있습니다)


1

^ 연산자의 결과가 MySQL에서 부호없는 64 비트 정수이므로 열에 부호있는 정수가 있다고 가정하면 CAST (a ^ b AS SIGNED)를 사용해야 할 수도 있습니다.

누군가에게 도움이되는 경우, 주어진 두 행 사이에서 동일한 열을 교환하는 데 사용한 방법은 다음과 같습니다.

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

여기서 $ 1과 $ 2는 두 행의 키이고 $ 3은 첫 번째 쿼리의 결과입니다.


1

나는 그것을 시도하지 않았지만

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

할 수 있습니다.


1

당신은 수있는 열 이름을 변경, 그러나 이것은 해킹의 더 많은 것이다. 그러나이 열에있을 수있는 색인에주의하십시오


1

테이블 이름은 고객입니다. 필드는 a와 b이며 값을 b로 바꿉니다 ..

고객 설정 업데이트 a = (@ temp : = a), a = b, b = @temp

나는 이것이 잘 작동하는지 확인했다.


1

SQL Server에서이 쿼리를 사용할 수 있습니다.

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

0

단일 쿼리를 사용하여 열 값 교환

업데이트 my_table SET a = @ tmp : = a, a = b, b = @ tmp;

건배...!


1
이것은 허용 된 답변 의 # 3의 반복입니다 .
Pang

0

아카이브와 같은 한 열에서 다른 열로 값을 이동하고 원래 열의 값을 재설정해야했습니다.
아래 (위의 답변에서 # 3 참조)가 저에게 효과적이었습니다.

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

이 예제 는 날짜가 잘못된 방식으로 레코드에 대해 start_dateend_date바꿉니다 (ETL을 주요 재 작성으로 수행 할 때 종료 날짜 보다 늦은 시작 날짜가 있습니다. 다운, 불량 프로그래머!).

현장에서, 나는 성능상의 이유로 MEDIUMINT를 사용하고 있습니다 (Julian 일과 같지만 0 루트는 1900-01-01 임) .WHERE mdu.start_date> mdu.end_date 조건을 사용하는 것이 좋습니다 .

PK는 3 개의 열 모두에 개별적으로있었습니다 (운영 / 인덱싱 이유로).

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;

참고 :이 코드는 0.203 초 동안 145 / 108,456 레코드를 업데이트했습니다. 일회성 작업이므로 성능이 중요하지 않았습니다.
앤드류 포스터

0

tb_user에서 성과 이름의 값을 바꾸고 싶다고 가정 해 봅시다.

가장 안전한 방법은 다음과 같습니다.

  1. tb_user를 복사하십시오. 따라서 tb_user와 tb_user_copy라는 2 개의 테이블이 있습니다.
  2. 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

아래 쿼리를 적용 할 수 있습니다. 그것은 나에게 완벽했습니다.

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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.