如何在MySQL中移动列值?

时间:2015-08-07 09:28:43

标签: mysql sql

我有这张桌子:

table1
+------+----------+-----------+----------+
|  id  | org1     | org2      | org3     |
+------+----------+-----------+----------+
|  1   | HR       | (NULL)    | Staff    |
+------+----------+-----------+----------+
|  2   | (NULL)   | IT        | Dev      |
+------+----------+-----------+----------+
|  3   | (NULL)   | (NULL)    | Finance  |
+------+----------+-----------+----------+

我想将所有值都移到左边,所以最终结果是:

table1
+------+----------+-----------+----------+
|  id  | org1     | org2      | org3     |
+------+----------+-----------+----------+
|  1   | HR       | Staff     | (NULL)   |
+------+----------+-----------+----------+
|  2   | IT       | Dev       | (NULL)   |
+------+----------+-----------+----------+
|  3   | Finance  | (NULL)    | (NULL)   |
+------+----------+-----------+----------+

有没有优雅的方式呢?

3 个答案:

答案 0 :(得分:3)

使用coalesce()和子查询

select id, o1, 
       CASE WHEN o2!=o1 THEN o2 END o2,
       CASE WHEN o3!=o2 THEN o3 END o3 
FROM
( select id, coalesce(org1,org2,org3) o1,
             coalesce(org2,org3)      o2,
                      org3            o3 from tbl ) t

<强>更新

之前的答案还不够,因为R2D2发现非常正确。不幸的是你不能在mysql中做CTE所以我创建了一个视图(我通过另一列org4扩展了示例):

CREATE VIEW vert AS 
select id i,1 n, org1 org FROM tbl where org1>'' UNION ALL
select id,2, org2 FROM tbl where org2>'' UNION ALL
select id,3, org3 FROM tbl where org3>'' UNION ALL
select id,4, org4 FROM tbl where org4>'';

使用此视图,现在可以执行以下操作:

SELECT id,
(select org from vert where i=id order by n limit 1) org1,
(select org from vert where i=id order by n limit 1,1) org2,
(select org from vert where i=id order by n limit 2,1) org3,
(select org from vert where i=id order by n limit 3,1) org4
FROM tbl

不漂亮,但它完成了工作,请参见此处:SQLfiddle

输入:

| id |   org1 |   org2 |    org3 |   org4 |
|----|--------|--------|---------|--------|
|  1 |     HR | (null) |   Staff |     IT |
|  2 | (null) |     IT |     Dev | (null) |
|  3 | (null) | (null) | Finance |     HR |

输出:

| id |    org1 |  org2 |   org3 |   org4 |
|----|---------|-------|--------|--------|
|  1 |      HR | Staff |     IT | (null) |
|  2 |      IT |   Dev | (null) | (null) |
|  3 | Finance |    HR | (null) | (null) |

答案 1 :(得分:1)

<强>更新

根据 cars10 回答,需要切换订单COALESCE(org2,org3)并考虑所有3列为NOT NULL

SELECT id, o1, 
       CASE WHEN o2!=o1 THEN o2 END o2,
       CASE WHEN o3!=o2 THEN o3 END o3 
FROM
(
SELECT id
,COALESCE(org1,org2,org3) o1
,IF((org1 IS NOT NULL) AND (org2 IS NOT NULL) AND (org3 IS NOT NULL),
    org2,
    COALESCE(org3,org2)
) o2
,org3 o3
FROM table1
) t

添加cars10提到的案例:

DROP TABLE IF EXISTS table1;
CREATE TABLE `table1` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `org1` VARCHAR(255) DEFAULT NULL,
  `org2` VARCHAR(255) DEFAULT NULL,
  `org3` VARCHAR(255) DEFAULT NULL,  
  PRIMARY KEY (`id`)
);

INSERT INTO `table1` VALUES ('1', NULL, 'IT', 'DEV');
INSERT INTO `table1` VALUES ('2', 'HR',NULL,'Staff');
INSERT INTO `table1` VALUES ('3', 'ID','Building',NULL);
INSERT INTO `table1` VALUES ('4', 'Support','Business','1st line');

INSERT INTO `table1` VALUES ('5','Finance', NULL, NULL);
INSERT INTO `table1` VALUES ('6', NULL, 'Finance', NULL );
INSERT INTO `table1` VALUES ('7', NULL, NULL, 'Finance');
INSERT INTO `table1` VALUES ('8', NULL, NULL, NULL);

http://www.sqlfiddle.com/#!9/cd969/1

正如 Thorsten Kettner 所提到的,没有优雅的方法可以做到这一点。 我发现上面一个是最短的工作解决方案。

答案 2 :(得分:1)

一个相当优雅的解决方案是首先使列行,使用分析函数对它们进行排名,然后汇总结果:

select 
  id, 
  max(case when rn = 1 then org end) as org1,
  max(case when rn = 2 then org end) as org2,
  max(case when rn = 3 then org end) as org3
from
(
  select id, org, row_number() over (partition by id order by num) as rn
  from
  (
    select id, org1 as org, 1 as num from mytable where org1 is not null
    union all
    select id, org2 as org, 2 as num from mytable where org2 is not null
    union all
    select id, org3 as org, 3 as num from mytable where org3 is not null
  ) given
) ranked
group by id;
然而,MySQL并不支持分析功能。所以在MySQL中没有使用纯SQL的优雅解决方案。你将不得不使用CASE WHEN和COALESCE,它只需要三列就可以快速完成,但是会更加无聊。

select 
  coalesce(org1, org2, org3) as org1,
  case when org1 is not null
    then coalesce(org2, org3) 
    else case when org2 is not null then org3 end
  end as org2
  case when org1 is not null and org2 is not null then org3 end as org3
from mytable;

另一个想法是编写一个存储过程来获取第n个非null值。这不再是纯SQL,我也不太了解MySQL,知道这是否可行:

select 
  nth_value(1, org1, org2, org3) as org1,
  nth_value(2, org1, org2, org3) as org2,
  nth_value(3, org1, org2, org3) as org3
from mytable;