Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Составление SQL-запросов > [MySQL] Выборка последних записей в GROUP BY


Автор: s1lver 3.2.2016, 12:11
Доброго времени суток!

Необходимо выбрать последние записи из таблицы для каждого идентификатора(количество идентификаторов может различаться каждый раз). 
Код

[1, 2, 3]


Сделал используя UNION так:

Код

(SELECT `avg_balance` FROM `product` WHERE nomenclature = 1 ORDER BY `date` DESC LIMIT 1)
UNION
(SELECT `avg_balance` FROM `product` WHERE nomenclature = 2 ORDER BY `date` DESC LIMIT 1)
UNION
(SELECT `avg_balance` FROM `product` WHERE nomenclature = 3 ORDER BY `date` DESC LIMIT 1)


Запрос возвращает правильные данные. Но, хотелось бы, используя IN, как-то так:

Код

SELECT `avg_balance`
FROM `product` 
WHERE nomenclature IN (1,2,3)
GROUP BY `nomenclature`
ORDER BY `date` DESC


В этом случае, тоже работает, но только, естественно, возвращает первые записи. 

Подскажите, пожалуйста, как можно поправить в этом случае?

Автор: Akina 3.2.2016, 12:16
См. http://www.sql.ru/forum/687908-a/faq-vyborka-pervoy-posledney-zapisi-v-gruppah.

Автор: tzirechnoy 3.2.2016, 12:23
(в приличных серверах второй код не заработает, кстати, поскольку поле в ORDER, да и поле в SELECT несгруппированы в запросе с группировкой)

Код
SELECT avg_balance, nomenclature 
  FROM product p1 
  WHERE nomenclature IN (1,2,3) 
    AND date=(SELECT max(date) 
                 FROM product p2 
                 WHERE p2.nomenclature=p1.nomenclature)


Есть, правда, такая деталь, что если дата неуникальна, то выдаст всё за последнюю дату, но это не всегда плохо по факту. А если плохо -- то можно ещё добавить фильтр по любому уникальному ключу: 
Код

  AND id=(SELECT max(id)
            FROM product p2
            WHERE p2.nomenclature=p1.nomenclature
                  AND p2.date=p1.date)


И вторая деталь -- часто в серверах проблема с тем, чтобы понять что оператор max требует одной выборки по ключу (и вообще с оптимизацыей групповых операторов), потому для скорости можэт иметь смысл переписать SELECT max(date) FROM ... WHERE на ваш SELECT date FROM ... WHERE ... ORDER BY date DESC LIMIT 1.

Автор: s1lver 3.2.2016, 12:29
Вот, еще вариант получился

Код

SELECT `s`.avg_balance
FROM (
    SELECT MAX(`id`) AS `max_id`
    FROM `product`
    GROUP BY `nomenclature`
    ORDER BY `date` DESC
 ) AS `a`
 LEFT JOIN `product` AS `s` ON `a`.`max_id` = `s`.`id`


Как бы, даже, возвращает правильные данные  smile

Добавлено @ 12:33
Akina, 
tzirechnoy, 

Теперь еще "хуже". Есть три варианта, которые возвращают нужные данные, проблема какой использовать.  smile 

Автор: Akina 3.2.2016, 12:57
Цитата(s1lver @  3.2.2016,  13:29 Найти цитируемый пост)
Есть три варианта, которые возвращают нужные данные

Внимательно прочитайте по ссылке варианты постановки задачи и проверьте, ту ли задачу решаете в каждом из вариантов.

Цитата(s1lver @  3.2.2016,  13:29 Найти цитируемый пост)
проблема какой использовать.

А вот это уже зависит от конкретной системы - начиная с наполнения таблиц и кончая аппаратной конфигой. 

Автор: s1lver 3.2.2016, 13:14
Цитата

Есть, правда, такая деталь, что если дата неуникальна, то выдаст всё за последнюю дату, но это не всегда плохо по факту. А если плохо -- то можно ещё добавить фильтр по любому уникальному ключу


Кстати, если необходимо выбрать записи за вчерашний день, то получается как-то так?
Код

SELECT `avg_balance`
FROM `product` p1
WHERE (`nomenclature` IN ('1', '2', '3')) 
AND (`date` = (
    SELECT MAX(`date`) 
    FROM `product` p2 
    WHERE p2.nomenclature = p1.nomenclature
    AND (p2.date >= (UNIX_TIMESTAMP(CURDATE()-1) - 24*60*60)) 
    AND (p2.date < UNIX_TIMESTAMP(CURDATE()))
    ) 
)


На первый взгляд, возвращаемые данные правильные 
Цитата

Внимательно прочитайте по ссылке варианты постановки задачи и проверьте, ту ли задачу решаете в каждом из вариантов.


Судя по всему, все правильно. smile 

Автор: tzirechnoy 3.2.2016, 13:17
Цитата
Как бы, даже, возвращает правильные данные 


Как бы нет. То есть он возвращает имено данные для самого большого id, а не самой большой даты. А ORDER BY -- вообще не работает, и правильный сервер об этом бы сказал.

Переделать на дату -- можно. Разница с подзапросом в WHERE будет в том, что в одном случае для поиска максимальных дат оптимизатор скорее всего один раз пройдёт по product, получив список дат по номенклатурам, а в другом -- столько раз, сколько нужно номенклатур, пройдёт по ключу, свзязанному с номенклатурой. В общем, по скорости можэт быть лучшэ и так и так -- в зависимости от селективности номенклатуры и количества их в одном запросе. Правда, подзапрос в WHERE можно переделать на ORDER BY ... LIMIT 1, и заставить использовать индэкс, а во FROM по-моему это тяжэловато.


Автор: Akina 3.2.2016, 14:33
Цитата(s1lver)

Код

p2.date >= (UNIX_TIMESTAMP(CURDATE()-1) - 24*60*60)


Ахринеть... а почему не просто просто 
Код

p2.date >= CURDATE() - INTERVAL 1 DAY

?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)