MYSQL получает первые (МИНИМАЛЬНЫЕ) и последние (МАКСИМАЛЬНЫЕ) записи заказа, сгруппированные по пользователям

#mysql

#mysql

Вопрос:

У меня есть таблица mysql, в которой хранятся все заказы клиентов следующим образом (упрощенно для вопроса):

  ---------- --------------- ------ 
| Field    | Type          | Null | 
 ---------- --------------- ------ 
| id (pk)  | int(10)       | NO   |
| cust_id  | int(10)       | NO   |
| total    | decimal(10,2) | NO   |
| created  | datetime)     | NO   |
 ---------- --------------- ------ 
  

В одном запросе я хочу получить первый заказ каждого пользователя и общее количество заказов, а также их самый последний заказ и общее количество заказов

Так что у меня должны быть такие результаты, как:

  ---------- ------------------ --------------- ------------------ --------------- 
| cust_id  | first_ord_total  | first_ord_date| last_ord_total   | last_ord_date |
 ---------- ------------------ --------------- ------------------ --------------- 
| 123      | 150.48           | 2018-03-01    | 742.25           | 2020-05-19    |
| 456      | 20.99            | 2019-08-01    | 67.22            | 2020-09-17    | 
| 789      | 259.99           | 2019-01-01    | 147.15           | 2020-08-31    | 
 ---------- ------------------ --------------- ------------------ --------------- 
  

Кажется, я могу получить даты первого и последнего заказа, используя MIN и MAX, но я не могу связать их обратно, чтобы также указать общую сумму заказа из того же заказа / записи

Я знаю, что это возможно, но я изо всех сил пытаюсь сделать это правильно

Ответ №1:

Если ваша версия MySQL поддерживает оконные функции, с помощью MIN(), MAX() и FIRST_VALUE():

 select distinct cust_id,
       first_value(total) over (partition by cust_id order by created) first_order_total,
       min(created) over (partition by cust_id) first_order_date,
       first_value(total) over (partition by cust_id order by created desc) last_order_total,
       max(created) over (partition by cust_id) last_order_date
from customers
  

Без оконных функций используйте запрос, который повторно включает даты первого и последнего заказа каждого клиента, и присоедините его к таблице, в которой вы используете условную агрегацию:

 select c.cust_id,
       max(case when c.created = t.min_created then c.total end) first_order_total,
       max(case when c.created = t.min_created then c.created end) first_order_date,
       max(case when c.created = t.max_created then c.total end) last_order_total,
       max(case when c.created = t.max_created then c.created end) last_order_date
from customers c 
inner join (
  select cust_id, min(created) min_created, max(created) max_created
  from customers
  group by cust_id  
) t on t.cust_id = c.cust_id and c.created in (t.min_created, t.max_created)
group by c.cust_id
  

Ответ №2:

В MySQL 8 ROW_NUMBER здесь пригодится:

 WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY cust_id ORDER BY created) rn_first,
              ROW_NUMBER() OVER (PARTITION BY cust_id ORDER BY created DESC) rn_last
    FROM orders
)

SELECT
    cust_id,
    MAX(CASE WHEN rn_first = 1 THEN total END)   AS first_ord_total,
    MAX(CASE WHEN rn_first = 1 THEN created END) AS first_ord_date,
    MAX(CASE WHEN rn_last = 1  THEN total END)   AS last_ord_total,
    MAX(CASE WHEN rn_last = 1  THEN created END) AS last_ord_date
FROM cte
GROUP BY
    cust_id;
  

Здесь используется stategy ROW_NUMBER для каждого клиента для идентификации первой и последней записей транзакций в CTE. Затем мы агрегируем по клиентам, чтобы найти первые и последние общие суммы и даты.

Комментарии:

1. Интересно, хотя следовало бы упомянуть, что на данный момент я привязан к 5.7.12