如何根据条件按组选择第一项?

时间:2019-06-14 18:55:54

标签: sql postgresql greatest-n-per-group

我有一个具有以下布局的表,用于存储用户的订单并记住当前正在处理的订单:

Sequence | User | Order | InProcess
---------+------+-------+----------
       1 |    1 |     1 |
       2 |    1 |     2 |
       3 |    2 |     1 |
       4 |    3 |     1 |
       5 |    1 |     3 |
       6 |    4 |     1 |
       7 |    2 |     2 |

例如,第4 | 3 | 1 |行表示用户3的第4个订单,这是他/她的1的订单。现在,我要选择接下来要处理的顺序。必须根据以下条件完成此操作:

  • 较旧的订单(具有较低的序列号)首先被处理。
  • 每个用户一次只能处理一个订单。
  • 一旦订单被选中处理,它将被标记为InProcess
  • 订单完成后,将从该列表中删除。

所以,一段时间后,它看起来可能像这样:

Sequence | User | Order | InProcess
---------+------+-------+----------
       1 |    1 |     1 | X
       2 |    1 |     2 |
       3 |    2 |     1 | X
       4 |    3 |     1 | X
       5 |    1 |     3 |
       6 |    4 |     1 |
       7 |    2 |     2 |

现在被要求下一个要处理的订单时,答案将是序列号为6的行,因为已经处理了用户1、2和3的订单,因此不能再处理其他订单。问题是:如何有效进入这一行?

基本上我需要的是与之等效的SQL

  

在所有订单中,选择第一个尚未处理且用户尚未处理订单的订单。

问题是如何用SQL告诉这个问题?顺便说一句:我正在寻找一种标准的SQL解决方案,而不是特定于DBMS的方法。但是,如果出于某种原因将问题限制在特定的DBMS上,那么这些就是我必须支持的(按此顺序):

  • PostgreSQL
  • MariaDB
  • MySQL
  • SQL Server
  • MongoDB

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

我认为可以捕捉到您的逻辑:

select t.*
from (select t.*, max(in_process) over (partition by user_id) as any_in_process
      from t
     ) t
where any_in_process is null
order by sequence
fetch first 1 row only;

获取一行是特定于数据库的,而其余的则是非常通用的。

答案 1 :(得分:0)

您可以使用ROW_NUMBER()窗口函数来获取下一个要处理的订单,如下所示:

select * 
from (
  select
    *,
    row_number() over(order by "order", "sequence") as as rn
  from t
  where "user" not in (
    select "user" from t where inprocess = 'X'
  )
) x 
where rn = 1

在PostgreSQL,MariaDB 10.2,MySQL 8.0,SQL Server 2012中可用。