按顺序合并多行

Merging multiple rows according to an order

假设有以下几行

| Id | MachineName | WorkerName | MachineState |
|----------------------------------------------|
| 1  | Alpha       | Young      |  RUNNING     |
| 1  | Beta        |            |  STOPPED     |
| 1  | Gamma       | Foo        |  READY       |
| 1  | Zeta        | Zatta      |              |
| 2  | Guu         | Niim       |  RUNNING     |
| 2  | Yuu         | Jaam       |  STOPPED     |
| 2  | Nuu         |            |  READY       |
| 2  | Faah        | Siim       |              |
| 3  | Iem         |            |  RUNNING     |
| 3  | Nyt         | Fish       |  READY       |
| 3  | Qwe         | Siim       |              |

我们想根据以下优先级合并这些行:

已停止 > 运行 > 就绪 >(空或空)

如果某行具有最高优先级的值,则应使用该行的值(仅当它不为空时)。如果它为空,则应使用来自任何其他行的值。行应按 id

分组

以上输入的正确输出是:

| Id | MachineName | WorkerName | MachineState |
|----------------------------------------------|
| 1  | Beta        | Foo        |  STOPPED     |
| 2  | Yuu         | Jaam       |  STOPPED     |
| 3  | Iem         | Fish       |  RUNNING     |

什么是好的 sql 查询来完成此任务?我尝试使用连接,但没有成功。

这是一个优先查询。一种方法使用变量。另一个使用 union all 。 . .如果给定 id 的状态不重复,则此方法有效:

select t.*
from table t
where machinestate = 'STOPPED'
union all
select t.*
from table t
where machinestate = 'RUNNING' and
      not exists (select 1 from table t2 where t2.id = t.id and t2.machinestate in ('STOPPED'))
union all
select t.*
from table t
where machinestate = 'READY' and
      not exists (select 1 from table t2 where t2.id = t.id and t2.machinestate in ('STOPPED', 'RUNNING'));

您可以将此视为分组最大问题的一个案例,前提是您可以在 MachineState 列上获得合适的排序——例如通过使用 CASE 表达式:

SELECT   a.Id,
         COALESCE(a.MachineName, t.MachineName) MachineName,
         COALESCE(a.WorkerName , t.WorkerName ) WorkerName,
         a.MachineState
FROM     myTable a JOIN (
           SELECT   Id,
                    MIN(MachineName) AS MachineName,
                    MIN(WorkerName ) AS WorkerName,
                    MAX(CASE MachineState
                      WHEN 'READY'   THEN 1
                      WHEN 'RUNNING' THEN 2
                      WHEN 'STOPPED' THEN 3
                    END) AS MachineState
           FROM     myTable
           GROUP BY Id
         ) t ON t.Id = a.Id AND t.MachineState = CASE a.MachineState
           WHEN 'READY'   THEN 1
           WHEN 'RUNNING' THEN 2
           WHEN 'STOPPED' THEN 3
         END

sqlfiddle 上查看:

| id | machinename | workername | machinestate |
|----|-------------|------------|--------------|
|  1 |        Beta |        Foo |      STOPPED |
|  2 |         Yuu |       Jaam |      STOPPED |
|  3 |         Iem |       Fish |      RUNNING |

如果 MachineStateENUM 类型的列(以适当的顺序定义),您可以避免使用 CASE 的痛苦。在这种情况下,对字符串值进行简单的字典顺序排序会产生相同的结果,但这是一个巧合,你真的不应该依赖它,因为当有人试图在未来。

将 MachineState 更改为枚举:

`MachineState` enum('READY','RUNNING','STOPPED') DEFAULT NULL

和sql很简单:

select t.id,state.machinename,state.workername,t.mstate from state,(select id,max(MachineState) mstate from state group by Id) t  where t.mstate=state.machinestate and t.id=state.id;