我有一个带坐标的MySQL表,列名是X和Y.现在我要交换此表中的列值,以便X变为Y而Y变为X.最明显的解决方案是重命名列,但我不想进行结构更改,因为我没有必要这样做。
这有可能以某种方式与 UPDATE 相关吗? UPDATE表SET X = Y,Y = X 显然不会做我想要的。
编辑:请注意,我对上述权限的限制有效地阻止了使用ALTER TABLE或其他更改表/数据库结构的命令。遗憾的是,重命名列或添加新列不是选项。
答案 0 :(得分:178)
我只需处理相同的事情,我将总结我的发现。
UPDATE table SET X=Y, Y=X
方法显然不起作用,因为它只会将两个值都设置为Y.
这是一个使用临时变量的方法。感谢Antony从http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/的评论中对“IS NOT NULL”的调整。没有它,查询工作无法预测。请参阅帖子末尾的表架构。如果其中一个为NULL,则此方法不会交换值。使用没有此限制的方法#3。
UPDATE swap_test SET x=y, y=@temp WHERE (@temp:=x) IS NOT NULL;
这种方法由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;
我提出的另一种方法似乎有效:
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);
答案 1 :(得分:46)
您可以使用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>
试一试!!!
答案 2 :(得分:20)
以下代码适用于我的快速测试中的所有方案:
UPDATE table swap_test
SET x=(@temp:=x), x = y, y = @temp
答案 3 :(得分:10)
UPDATE表SET X = Y,Y = X 将完全符合您的要求(编辑:在PostgreSQL中,而不是MySQL,见下文)。这些值取自旧行并分配给同一行的新副本,然后替换旧行。您不必使用临时表,临时列或其他交换技巧。
@ D4V360:我明白了。这令人震惊和意外。我使用PostgreSQL,我的答案在那里正常工作(我试过)。请参阅PostgreSQL UPDATE docs(在参数,表达式下),其中提到SET子句右侧的表达式明确使用旧的列值。我看到相应的MySQL UPDATE docs包含声明“单表UPDATE赋值通常从左到右进行评估”,这意味着您描述的行为。
很高兴知道。
答案 4 :(得分:5)
好的,只是为了好玩,你可以做到这一点! (假设您正在交换字符串值)
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 :(得分: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)
答案 6 :(得分:4)
我相信有一个中间交换变量就是这样的最佳实践:
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,你也不能使用sum来溢出的可能性。
和
update z set c1 = c2, c2 = @c where @c := c1
无效 如果c1为0或NULL或零长度字符串或只是空格。
我们需要将其更改为
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)
答案 7 :(得分:4)
两种选择 1.使用临时表 2.调查 XOR algorithm
答案 8 :(得分:2)
这肯定有效!我只是需要它来交换欧元和SKK价格列。 :)
UPDATE tbl SET X=Y, Y=@temp where @temp:=X;
以上操作无效(ERROR 1064(42000):您的SQL语法出错)
答案 9 :(得分:1)
你可以更改列名,但这更像是一个黑客攻击。但要谨慎对待这些列上的任何索引
答案 10 :(得分:1)
我没试过,但是
UPDATE tbl SET @temp=X, X=Y, Y=@temp
可能会这样做。
标记
答案 11 :(得分:1)
假设您已在列中签署了整数,则可能需要使用CAST(a ^ b AS SIGNED),因为^运算符的结果是MySQL中的无符号64位整数。
如果它对任何人有帮助,这里是我用来在两个给定行之间交换相同列的方法:
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是第一次查询的结果。
答案 12 :(得分:0)
让我们想象一下这个表,让我们尝试交换'sex'表中的m和f
| id |名称|性别|薪水|
| ---- | ------ | ----- | -------- |
| 1 | A |米| 2500 |
| 2 | B | f | 1500 |
| 3 | C |米| 5500 |
| 4 | D | f | 500 |
更新性别
SET性别=案例性
当“ m”然后“ f”
ELSE'm'
结束;
因此,更新后的表将变为:
| id |名称|性别|薪水|
| ---- | ------ || ----- | -------- |
| 1 | A | f | 2500 |
| 2 | B |米| 1500 |
| 3 | C | f | 5500 |
| 4 | D |米| 500 |
答案 13 :(得分:0)
如果要交换x到y和y到x的所有列;使用此查询。
UPDATE table_name SET column_name = CASE column_name WHERE 'value of col is x' THEN 'swap it to y' ELSE 'swap it to x' END;
答案 14 :(得分: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;
答案 15 :(得分:0)
假设您要交换tb_user中名字和姓氏的值。
最安全的是:
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
答案 16 :(得分:0)
在SQL Server中,您可以使用以下查询:
update swaptable
set col1 = t2.col2,
col2 = t2.col1
from swaptable t2
where id = t2.id
答案 17 :(得分:0)
表名是客户。 字段是a和b,将值交换为b;。
更新客户SET a =(@ temp:= a),a = b,b = @temp
我检查了它是否工作正常。
答案 18 :(得分:0)
此示例将开始日期和结束日期交换为日期日期错误的记录(当执行ETL进行重大重写时,我发现一些开始< / strong>日期晚于其结束日期。糟糕的程序员!)。
就地而言,出于性能原因,我正在使用MEDIUMINT(例如朱利安天数,但0的根为1900-01-01),所以我可以在 mdu.start_date> mdu的条件下工作。结束日期。
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;
答案 19 :(得分:0)
我只需将值从一列移动到另一列(如归档)并重置原始列的值。
以下(上面接受的答案中#3的引用)对我有用。
Update MyTable set X= (@temp:= X), X = 0, Y = @temp WHERE ID= 999;
答案 20 :(得分: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;
答案 21 :(得分:0)
使用单个查询交换列值
UPDATE my_table SET a = @ tmp:= a,a = b,b = @ tmp;
欢呼声......!