在MySQL中交换列值

时间:2020-03-05 18:45:53  来源:igfitidea点击:

我有一个带有坐标的MySQL表,列名称为X和Y。现在我想交换该表中的列值,以使X变为Y而Y变为X。最明显的解决方案是重命名列,但是我不想进行结构更改,因为我不一定有这样做的权限。

这可能以某种方式与UPDATE有关吗?更新表SET X = Y,Y = X显然不会满足我的要求。

编辑:请注意,我上面提到的权限限制有效地防止了使用ALTER TABLE或者其他更改表/数据库结构的命令。不幸的是,重命名列或者添加新列不是选项。

解决方案

回答

两种选择
1.使用临时表
2.调查
XOR算法

回答

`

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)

回答

UPDATE表SET X = Y,Y = X将完全满足要求(编辑:在PostgreSQL中,而不是MySQL,请参见下文)。这些值取自旧行,并分配给同一行的新副本,然后替换旧行。我们不必求助于使用临时表,临时列或者其他交换技巧。

@ D4V360:我知道了。这是令人震惊和始料未及的。我使用PostgreSQL,我的答案在那里正确工作(我尝试过)。请参见PostgreSQL UPDATE文档(在Parameters(表达式)下),其中提到SET子句右侧的表达式显式使用列的旧值。我看到相应的MySQL UPDATE文档包含语句"通常从左到右评估单表UPDATE分配",这表示我们描述的行为。

很高兴知道。

回答

好的,所以只是为了好玩,我们可以这样做! (假设我们要交换字符串值)

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;

回答

假设列中有带符号的整数,则可能需要使用CAST(a ^ b AS SIGNED),因为^运算符的结果是MySQL中无符号的64位整数。

万一它对任何人都有帮助,这是我用来在两个给定行之间交换同一列的方法:

SELECT BIT_XOR(foo) FROM table WHERE key =  OR key = 

UPDATE table SET foo = CAST(foo ^  AS SIGNED) WHERE key =  OR key = 

其中$ 1和$ 2是两行的键,而$ 3是第一个查询的结果。

回答

我没有尝试过

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

可能会做。

标记

回答

这肯定有效!我只是需要它来交换欧元和SKK价格栏。 :)

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

上面的方法不起作用(错误1064(42000):SQL语法有错误)

回答

我只需要处理相同的问题,然后我将总结我的发现。

  • UPDATE表SET X = Y,Y = X`的方法显然不起作用,因为它将两个值都设置为Y。
  • 这是使用临时变量的方法。感谢http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/的注释中的" IS NOT NULL"调整。没有它,查询将无法正常工作。请参阅文章末尾的表架构。如果其中一个为NULL,则此方法不交换值。使用没有此限制的方法#3. UPDATE swap_test SET x = y,y = @ temp(@temp:= x)不为空;
  • 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);

回答

以下代码适用于我的快速测试中的所有方案:

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

回答

我们可以更改列名,但这更像是一个技巧。但请注意这些列上可能包含的任何索引