按顺序合并多行
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 |
如果 MachineState
是 ENUM
类型的列(以适当的顺序定义),您可以避免使用 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;
假设有以下几行
| 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 |
如果 MachineState
是 ENUM
类型的列(以适当的顺序定义),您可以避免使用 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;