Выберите состояние последнего заказа из заказов

#mysql #sql

#mysql #sql

Вопрос:

У меня есть таблицы:

заказы:

 id_order    id_customer    
1           1              
2           2              
3           1              
  

orders_history

 id_history  id_order    id_order_state    date_add
1           1           1                 2010-01-01 00:00:00
2           1           2                 2010-01-02 00:00:00
3           1           3                 2010-01-03 00:00:00
4           2           2                 2010-05-01 00:00:00
5           2           3                 2011-05-02 00:00:00
6           3           1                 2011-05-03 00:00:00
7           3           2                 2011-06-01 00:00:00
  

order_state

 id_order_state    name
1                 New
2                 Sent
3                 Rejected
4                 ...
  

Как получить все order_id, где последний id_order_state этого заказа (под последним я подразумеваю это с MAX (id_history) или MAX (date_add)) не равен 1 или 3?

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

1. Можете ли вы опубликовать желаемый результат?

Ответ №1:

 select oh.id_history, oh.id_order, oh.id_order_state, oh.date_add
from (
    select id_order, max(date_add) as MaxDate
    from orders_history
    where id_order_state not in (1, 3)
    group by id_order 
) ohm
inner join orders_history oh on ohm.id_order = oh.id_order 
    and ohm.MaxDate = oh.date_add
  

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

1. Для справки, этот тип проблемы часто называют проблемой «groupwise max».

2. @TehShrike Странно, но я не получаю надлежащего результата: я думаю, что есть проблема с получением МАКСИМАЛЬНОЙ даты. То же самое с ответом @Tom H.

3. @dario каков тип данных date_add столбца?

4. @dario — Тогда я не могу вам помочь, либо что-то серьезно нарушено, либо вы запрашиваете другую базу данных / таблицу, чем вы думаете…

5. @RedFilter Я могу предоставить вам доступ phpmydamin к тестовой базе данных, если у вас есть немного времени.

Ответ №2:

Я думаю, что ему нужно, чтобы заказы были завершены… т.е. их окончательный статус, а не те, которые не относятся конкретно к 1 и 3. Первым предварительным запросом должен быть максимальный идентификатор независимо от кода состояния

 select 
      orders.*
   from
      ( select oh.id_order, 
               max( oh.id_history ) LastID_HistoryPerOrder
           from
               orders_history oh
           group by
               oh.id_order ) PreQuery
      join orders_history oh2
         on PreQuery.ID_Order = oh2.id_order
         AND PreQuery.LastID_HistoryPerOrder = oh2.id_history
         AND NOT OH2.id_order_state IN (1, 3)   <<== THIS ELIMINATES 1's amp; 3's from result set
      join Orders                               <<= NOW, anything left after above ^ is joined to orders
         on PreQuery.ID_Order = Orders.ID_Order
  

Просто чтобы повторно отобразить ВАШИ данные… Я отметил последнюю ПОСЛЕДОВАТЕЛЬНОСТЬ (ID_History) для каждого ЗАКАЗА… Это то, что вернет ПРЕДВАРИТЕЛЬНЫЙ запрос…

 id_history  id_order    id_order_state    date_add
  1           1          1                2010-01-01 00:00:00
  2           1          2                2010-01-02 00:00:00   
**3           1          3                2010-01-03 00:00:00

  4           2          2                2010-05-01 00:00:00   
**5           2          3                2011-05-02 00:00:00

  6           3          1                2011-05-03 00:00:00   
**7           3          2                2011-06-01 00:00:00
  

Результатом «предварительного запроса» будет следующее подмножество

 ID_Order   LastID_HistoryPerOrder (ID_History)
1          3  (state=3)  THIS ONE WILL BE SKIPPED IN FINAL RESULT
2          5  (state=3)  THIS ONE WILL BE SKIPPED IN FINAL RESULT
3          7  (state=2)
  

Теперь результат этого затем повторно присоединяется к истории заказов только для этих двух элементов… тем не менее, добавлены критерии для ИСКЛЮЧЕНИЯ 1,3 записей для «состояния заказа».

В этом случае,

    1 would be rejected as its state = 3 (sequence #3), 
   2 would be rejected since its last history is state = 3 (sequence #5).  
   3 would be INCLUDED since its state = 2 (sequence #7)  
  

Наконец, все, что присоединено к orders, приведет к ОДНОМУ идентификатору и будет хорошо сопоставляться с таблицей orders только по Order_ID и получать желаемые результаты.

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

1. @DRapp но для id_order_state = 2 он должен возвращать 3 в качестве последнего id_order_state. Для id_order = 3 он должен возвращать 2.

2. @dario, пожалуйста, пересмотрите этот ответ. Я изменил название столбца результатов предварительного запроса, чтобы прояснить, что я изначально имел в виду под «LastSequence». Теперь это «LastID_HistoryPerOrder». Кроме того, в последнем запросе у меня есть две строки комментария <<==, которые следует удалить. Это должно помочь уточнить, КОГДА 1 и 3 удалены.

3. @DRapp Пожалуйста, взгляните на мой пост с результатами вашего запроса.

4. @Dario, ты неправильно разместил мой запрос в двух частях… Предложение NOT IN не должно было быть В oh2.id_order_state В (1,3)… у вас было только (3). Далее, группировка по ВНУТРЕННЕМУ ПРЕДВАРИТЕЛЬНОМУ ЗАПРОСУ… У вас есть заказ по идентификатору ИСТОРИИ… Нет .. Просто ID_ORDER и вы получите то, что ожидаете… Я действительно попробовал это с вашими данными и получил то, что ожидалось … одна запись … заказ # 3

Ответ №3:

Другое возможное решение:

 SELECT DISTINCT
    id_order
FROM
    Orders_History OH1
LEFT OUTER JOIN Orders_History OH2 ON
    OH2.id_order = OH1.id_order AND
    OH2.is_order_state IN (1, 3) AND
    OH2.date_add >= OH1.date_add
WHERE
    OH2.id_order IS NULL
  

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

1. Например, для набора, имеющего: date_add: 2011-05-11 22:36:20 и 2011-04-16 23: 21:24, он выдает мне вторую максимальную дату, а затем неправильный id_state

2. Я не уверен, что вы имели в виду под этим, но если вы пытались использовать метод LEFT JOIN для нахождения экстремальных значений, то, я думаю, вы немного ошиблись. Это должно быть ...ON OH2.id_order = OH1.id_order AND OH2.date_add > OH1.date_add WHERE OH1.is_order_state NOT IN (1, 3) AND OH2.id_order IS NULL .

3. @andriy-m Вы правы! @Tom-h пожалуйста, исправьте свой ответ, как сказал andriy-m, и он будет завершен 🙂

Ответ №4:

Я использую «ответ на мой вопрос», потому что мне нужно опубликовать результаты ваших запросов. So.

К сожалению, не все ваши ответы, ребята, работают. Давайте подготовим тестовую среду:

 CREATE TABLE `order_history` (
`id_order_history` int(11) NOT NULL AUTO_INCREMENT,
`id_order` int(11) NOT NULL,
`id_order_state` int(11) NOT NULL,
`date_add` datetime NOT NULL,
PRIMARY KEY (`id_order_history`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin2;

CREATE TABLE `orders` (
`id_order` int(11) NOT NULL AUTO_INCREMENT,
`id_customer` int(11) DEFAULT NULL,
PRIMARY KEY (`id_order`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=latin2;

INSERT INTO `order_history` 
(`id_order_history`, `id_order`, `id_order_state`,     `date_add`) VALUES 
(1,1,1,'2011-01-01 00:00:00'),
(2,1,2,'2011-01-01 00:10:00'),
(3,1,3,'2011-01-01 00:20:00'),
(4,2,1,'2011-02-01 00:00:00'),
(5,2,2,'2011-02-01 00:25:01'),
(6,2,3,'2011-02-01 00:25:59'),
(7,3,1,'2011-03-01 00:00:01'),
(8,3,2,'2011-03-01 00:00:02'),
(9,3,3,'2011-03-01 00:01:00'),
(10,3,2,'2011-03-02 00:00:01');
COMMIT;

INSERT INTO `orders` (`id_order`, `id_customer`) VALUES 
(1,1),
(2,2),
(3,3),
(4,4),
(5,5),
(6,6),
(7,7);
COMMIT;
  

Теперь давайте выберем последнее / максимальное состояние для каждого заказа, поэтому давайте запустим простой запрос:

  select id_order, max(date_add) as MaxDate
 from `order_history`
 group by id_order
  

это дает нам ПРАВИЛЬНЫЕ результаты, сейчас нет ракетостроения:

 id_order         MaxDate
--------- -------------------
1         2011-01-01 00:20:00  //last order_state=3
2         2011-02-01 00:25:59  //last order_state=3
3         2011-03-02 00:00:01  //last order_state=2
  

Теперь для простоты, чтобы не изменять наши запросы для получения заказов, где последнее состояние не равно 3.

Мы ожидаем получить результат в одну строку с id_order = 3.

Итак, давайте протестируем наши запросы:

ЗАПРОС 1, выполненный RedFilter:

 select oh.id_order, oh.id_order_state, oh.date_add
from (
    select id_order, max(date_add) as MaxDate
    from `order_history`
    where id_order_state not in (3)
    group by id_order
) ohm
inner join `order_history` oh on ohm.id_order = oh.id_order 
and ohm.MaxDate = oh.date_add
  

Результат:

 id_order    id_order_state    date_add
-------------------------------------------------
1           2                 2011-01-01 00:10:00
2           2                 2011-02-01 00:25:01
3           2                 2011-03-02 00:00:01
  

Так что это неправда

ЗАПРОС 2, выполненный Томом Х.:

 SELECT DISTINCT OH1.id_order
FROM order_history OH1
LEFT OUTER JOIN order_history OH2 ON
  OH2.id_order = OH1.id_order AND
  OH2.id_order_state NOT IN (3) AND
  OH2.`id_order_history` >= OH1.`id_order_history`
WHERE
  OH2.id_order IS NULL
  

Результат:

 id_order
--------
1
2
  

Так что это неправда

Любые предложения приветствуются.

Редактировать

Благодаря комментарию Andriy M. у нас есть правильное решение. Это модификация запроса Тома Х. все должно выглядеть следующим образом:

 SELECT DISTINCT
OH1.id_order
FROM
  order_history OH1
LEFT OUTER JOIN order_history OH2 ON
  OH2.id_order = OH1.id_order
AND OH2.date_add > OH1.date_add 
WHERE OH1.id_order_state NOT IN (3) AND OH2.id_order IS NULL
  

РЕДАКТИРОВАТЬ 2:

ЗАПРОС 3, выполненный DRapp:

 select
  distinct orders.`id_order`
from
  ( select oh.id_order,
           max( oh.id_order_history ) LastID_HistoryPerOrder
       from
           order_history oh
       group by
           oh.id_order ) PreQuery
  join order_history oh2
     on PreQuery.id_order = oh2.id_order
     AND PreQuery.LastID_HistoryPerOrder = oh2.id_order_history
     AND NOT oh2.id_order_state IN (1,3)  
join orders                              
  on PreQuery.id_order = orders.id_order
  

Результат:

 id_order
--------
3
  

Итак, это, наконец, правда