#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, упс. Спасибо, что уловили это. Это исправлено.