为每个客户选择第一个和第 n 个订单

Posted

技术标签:

【中文标题】为每个客户选择第一个和第 n 个订单【英文标题】:Select first and nth order for each client 【发布时间】:2021-02-23 20:11:19 【问题描述】:

我正在尝试为每个用户显示第一个和第五个 order_id。

表格如下:

+----------+-----------+-------------------+
| Order_ID | Client_ID | Datetime          |
+----------+-----------+-------------------+
| 1        | 1         | YYYYMMDD HH:MM:SS |
+----------+-----------+-------------------+
| 2        | 1         | YYYYMMDD HH:MM:SS |
+----------+-----------+-------------------+
| 3        | 2         | YYYYMMDD HH:MM:SS |
+----------+-----------+-------------------+

我是这样写的:

select 
  t.client_id, 
  t.order_id as first_order, 
  t2.order_id as fifth_order,
  t.datetime as first_dt,
  t2.datetime as fifth_dt,
from
(
  select o.client_id, o.order_id, o.datetime,
  row_number() over(partition by o.client_id order by o.datetime) as rn
  from "OhMyTable" as o
) as t
  left join
    (
  select o.client_id, o.order_id, o.datetime,
  row_number() over(partition by o.client_id order by o.order_id) as rn
  from "OhMyTable" as o
      order by o.order_id
  ) as t2
  on t.client_id = t2.client_id
where t.rn = 1 and t2.rn = 5

但我希望看到那些下了第一单但没有五分之一的客户。应该有NULL,但我不明白怎么做。 :c

【问题讨论】:

【参考方案1】:

使用条件聚合:

select o.client_id,
       min(datetime) as first_datetime,
       max(datetime) as fifth_datime,
       max(case when seqnum = 1 then order_id end) as first_orderid,
       max(case when seqnum = 5 then order_id end) as fifth_orderid
from (select o.*
             row_number() over(partition by o.client_id order by o.datetime) as seqnum
      from "OhMyTable" o
     ) o
where seqnum in (1, 5);

【讨论】:

【参考方案2】:

如果发现条件聚合比两个子查询更简单。您可以使用having 子句进行过滤:

select client_id,
    max(order_id) filter(where rn = 1) first_order,
    max(order_id) filter(where rn = 5) fifth_order,
    min(datetime) first_dt,
    max(datetime) fifth_dt
from (
    select o.client_id, o.order_id, o.datetime,
        row_number() over(partition by o.client_id order by o.datetime) as rn
    from "OhMyTable" as o
) t
where rn in (1, 5)
group by client_id
having count(*) = 2

【讨论】:

【参考方案3】:

where t2.rn = 5 条件放入left join

select 
  t.client_id, 
  t.order_id as first_order, 
  t2.order_id as fifth_order,
  t.datetime as first_dt,
  t2.datetime as fifth_dt,
from
(
  select o.client_id, o.order_id, o.datetime,
  row_number() over(partition by o.client_id order by o.datetime) as rn
  from "OhMyTable" as o
) as t
  left join
    (
  select o.client_id, o.order_id, o.datetime,
  row_number() over(partition by o.client_id order by o.order_id) as rn
  from "OhMyTable" as o
      order by o.order_id
  ) as t2
  on t.client_id = t2.client_id
  and t2.rn = 5
where t.rn = 1 and t2.datetime is null

【讨论】:

以上是关于为每个客户选择第一个和第 n 个订单的主要内容,如果未能解决你的问题,请参考以下文章

SQL 他们的第一个订单和第二个订单的购买价值之间的差异

我将如何做第一部分和第二部分?

SQL:查找第 n 个客户的第 n 个订单

将第一个 SQL 选择语句结果用于第二个选择语句

在大小为 N 的数组的每 k 个元素中查找最小和第二小的元素

排序-选择排序