SQL — Группировать по тому, где строки имеют самую последнюю дату

#mysql #sql #database

#mysql #sql #База данных

Вопрос:

У меня есть следующие таблицы, и мне было интересно, как выполнить запрос к нему.

Рецепт таблицы

 ID | Name
0  | Apple pie
1  | Pizza
  

Ингредиент таблицы

 RecipeID  | Timestamp | Name    | Price
0         | 10-2-2014 | Apple   | 1.20
0         | 7-2-2014  | Apple   | 1.14
0         | 9-2-2014  | Flour   | 2.00
1         | 9-2-2014  | Tomato  | 1.12
  

Как я могу создать группу по RecipeID для этого, чтобы я получил следующие результаты

 RecipeID  | Name      | Total_Latest_Ingredient_Price
0         | Apple Pie | 3.20
1         | Pizza     | 1.12
  

Идея заключается в том, что запрос должен принимать только ингредиенты с самыми последними датами. Так что будет взята цена за яблоко от 10-2-2014, а не за одно (или оба) от 7-2-2014. Так что только самые последние даты.

Это может быть жестким файлом cookie (или нет! ) но я надеюсь, что вы сможете мне помочь!

Ответ №1:

Предполагая, что комбинация (RecipeID, Timestamp, Name) уникальна в Ingredient таблице, и предполагая, что столбец с именем Timestamp хранится в канонической форме (например, либо MySQL DATE, DATETIME, тип данных МЕТКИ ВРЕМЕНИ, либо символьный тип данных с таким форматом, что сравнение значений всегда будет давать «последнее» значение метки времени…

Нормативный шаблон заключается в использовании встроенного представления для извлечения «последней» Timestamp , а затем с помощью операции ОБЪЕДИНЕНИЯ для извлечения всей строки.

 SELECT g.RecipeID
     , SUM(g.Price) AS `Total_Latest_Ingredient_Price`
  FROM Ingredient g
  JOIN (SELECT h.RecipeID
             , h.Name
             , MAX(h.Timestamp) AS `Timestamp`
          FROM Ingredient h
         GROUP
            BY h.RecipeID
             , h.Name
       ) i
    ON i.RecipeID  = g.RecipeID
   AND i.Name      = g.Name
   AND i.Timestamp = g.Timestamp
 GROUP BY g.RecipeID
  

Встроенное представление с псевдонимом i получает «последнее» Timestamp (опять же, предполагая, что Timestamp столбец имеет каноническую форму, так что «максимальное» значение гарантированно будет «последним» значением. Это будет верно, если тип данных названного столбца Timestamp — MySQL DATE , DATETIME , или TIMESTAMP .)

Внешний запрос ссылается на строки, возвращаемые i, и выполняет операцию объединения для извлечения всей строки из таблицы (с псевдонимом g), чтобы получить соответствующую цену.

Чтобы получить Name столбец из таблицы рецептов, мы добавили бы в эту таблицу операцию объединения…

 SELECT g.RecipeID
     , r.Name
     , SUM(g.Price) AS `Total_Latest_Ingredient_Price`
  FROM Ingredient g
  JOIN (SELECT h.RecipeID
             , h.Name
             , MAX(h.Timestamp) AS `Timestamp`
          FROM Ingredient h
         GROUP
            BY h.RecipeID
             , h.Name
       ) i
    ON i.RecipeID  = g.RecipeID
   AND i.Name      = g.Name
   AND i.Timestamp = g.Timestamp
  JOIN Recipe r
    ON r.ID = g.RecipeID 
 GROUP BY g.RecipeID
  

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

1. Да, именно так я бы это сделал (и сделал это) тоже.

2. Я забыл добавить GROUP BY предложение к самому внешнему запросу; теперь это исправлено.

Ответ №2:

Приведенный выше запрос VMai близок, но вам нужно отфильтровать последнюю дату… итак, выберите максимальную временную метку для данного рецепта / ингредиента и присоедините ее к своей таблице. Вы также должны иметь возможность делать это с помощью предложения WHERE, но внутреннее соединение с таблицей ингредиентов (с максимальной отметкой времени по рецепту / ингредиенту) было более простым, поэтому я просто пошел с этим

  SELECT
        I.RecipeID,
        R.Name,
        SUM(Price) AS Total_Ingredient_Price
    FROM
        Recipe R
    INNER JOIN
        Ingredient I
    ON
        R.ID = I.RecipeID
    INNER JOIN (SELECT MAX(Timestamp) AS latest, RecipeID, Name FROM Ingredient GROUP BY RecipieID, Name) AS c
    ON I.RecipeID = c.RecipeID
    AND I.Name = c.Name
    AND I.TimeStamp = c.latest
    GROUP BY
        I.RecipeID,
        R.Name
  

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

1. . 1 … предполагая, что MAX (Timestamp) получит «последнее» значение.

2. Согласовано. Если временная метка не является фактическим значением даты, а скорее строкой (например), то это не сработает. Но преобразование таблицы данных в большинстве случаев довольно просто, и сама логика будет такой же, за исключением преобразования данных.

3. Данные не обязательно должны быть преобразованы в таблице. Этот запрос будет работать, если он хранится в каноническом формате, например, всегда точно в формате ‘ГГГГ-ММ-ДД’; если значение хранится как m / d / гггг, тогда мы могли бы использовать STR_TO_DATE функцию в запросе …, например MAX(STR_TO_DATE(d.Timestamp,'%m/%d/%Y'))

Ответ №3:

Это просто: вы суммируете цены на ингредиенты по рецепту:

 SELECT
    I.RecipeID,
    R.Name,
    SUM(Price) AS Total_Latest_Ingredient_Price
FROM
    Recipe R
INNER JOIN
    Ingredient I
ON
    R.ID = I.RecipeID
GROUP BY
    I.RecipeID,
    R.Name
  

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

1. Ммм, это выглядит хорошо! Мне просто интересно, где в вашем запросе вы учитываете различия во временных метках, которые могут быть там? Я имею в виду пример Apple, цена которого была определена в два момента. И брать только самую последнюю?

2. Однако в этом запросе не будет сохранена последняя цена …. в частности, в образце данных Apple будет суммироваться дважды

Ответ №4:

Еще одна реализация:

 select a.recipeid, c.name, sum(a.price) total_latest_ingredient_price
from ingredient a
join (
  select recipeid, name, max(timestamp) m
  from ingredient 
  group by recipeid, name) b on a.recipeid = b.recipeid and a.name = b.name and a.timestamp = b.m
join recipe c on a.recipeid = c.id
group by a.recipeid;
  

скрипка

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

1. думаю, вам нужно добавить b.name в вашей группе по 🙂

2. @evanv, я думаю, что они относятся к одному и тому же?

3. Вы абсолютно правы. Но вместо b.name в вашем запросе это должно быть c.name (Я перепутал сумму по ингредиентам с суммой по рецепту). В этом случае .recipeid, c .name было бы идеально …. и группа по a.recipe (или используйте c.recipe для консистенции… в любом случае, это не будет иметь большого значения… возможно, вместо этого используйте c, поскольку это, вероятно, основной индекс, но в остальном никакой реальной разницы в любом случае)

4. @evanv, упс. Спасибо, что уловили это. Это исправлено.