У меня есть эта таблица (myt):
CREATE TABLE myt (
name VARCHAR(50),
food VARCHAR(50),
d1 INT
);
INSERT INTO myt (name, food, d1) VALUES
('john', 'pizza', 2010),
('john', 'pizza', 2011),
('john', 'cake', 2012),
('tim', 'apples', 2015),
('david', 'apples', 2020),
('david', 'apples', 2021),
('alex', 'cookies', 2005),
('alex', 'cookies', 2006);
name food d1 food_year
john pizza 2010 2010
john pizza 2011 2011
john cake 2012 2012
tim apples 2015 2015
david apples 2020 2020
david apples 2021 2021
alex cookies 2005 2005
alex cookies 2006 2006
Я написал следующий запрос, чтобы узнать процентное соотношение каждого продукта по названию:
WITH FoodCounts AS (
SELECT name,
food,
COUNT(*) as food_count
FROM myt
GROUP BY name, food
),
TotalCounts AS (
SELECT name,
COUNT(*) as total_count
FROM myt
GROUP BY name
)
SELECT fc.name,
fc.food,
(fc.food_count * 100.0) / tc.total_count as percentage
FROM FoodCounts fc
JOIN TotalCounts tc
ON fc.name = tc.name;
name food percentage
alex cookies 100.00000
david apples 100.00000
john cake 33.33333
john pizza 66.66667
tim apples 100.00000
Сейчас я пытаюсь изменить этот запрос, чтобы узнать совокупные проценты. Например, по состоянию на 2011 год: каков был график питания Джона? Каков был график питания Джона по состоянию на 2012 год?
Чтобы ответить на этот вопрос, я попытался написать серию CTE, используя оконные функции:
WITH YearlyFoodCounts AS (
SELECT name,
food,
food_year,
COUNT(*) as food_count
FROM myt
GROUP BY name, food, food_year
),
CumulativeCounts AS (
SELECT name,
food_year,
SUM(food_count) OVER (PARTITION BY name ORDER BY food_year) as cumulative_count
FROM YearlyFoodCounts
)
SELECT yfc.name,
yfc.food,
yfc.food_year,
yfc.food_count,
cc.cumulative_count,
(yfc.food_count * 100.0) / cc.cumulative_count as percentage
FROM YearlyFoodCounts yfc
JOIN CumulativeCounts cc
ON yfc.name = cc.name AND yfc.food_year = cc.food_year
ORDER BY yfc.name, yfc.food_year;
Результат выглядит в правильном формате:
name food food_year food_count cumulative_count percentage
alex cookies 2005 1 1 100.00000
alex cookies 2006 1 2 50.00000
david apples 2020 1 1 100.00000
david apples 2021 1 2 50.00000
john pizza 2010 1 1 100.00000
john pizza 2011 1 2 50.00000
john cake 2012 1 3 33.33333
tim apples 2015 1 1 100.00000
Это правильный подход к этой проблеме?
@ Charlieface: спасибо за ответ. Давайте предположим, что невозможно иметь несколько разных блюд на одно имя, food_year. В таком случае, правильный ли мой запрос?
не могли бы вы показать мне, как адаптировать запрос к этой ситуации, которую вы описали?
Не совсем потому, что трудно понять, чего ты хочешь. Как рассчитать процент, если их несколько на name, food_year? Сделайте это вручную и расскажите мне, какие шаги вы сделали.


Вы это слишком усложняете. Ему не нужны соединения или подзапросы, вы можете сделать это на одном уровне, используя оконные функции. Вы можете поместить обычный агрегат внутри оконной функции, поскольку оконные функции выполняются после обычного агрегирования.
Примечание:
ROWS UNBOUNDED PRECEDING, потому что по умолчанию используется RANGE UNBOUNDED PRECEDING, что немного отличается.food. Поэтому вам следует агрегировать только по этим двум столбцам.SELECT
name,
MIN(food) AS food,
food_year,
COUNT(*) as food_count,
SUM(COUNT(*)) OVER (PARTITION BY name ORDER BY food_year ROWS UNBOUNDED PRECEDING) as cumulative_count,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY name ORDER BY food_year ROWS UNBOUNDED PRECEDING) as percentage
FROM myt
GROUP BY
name,
food_year;
Что произойдет, если на каждый
foodприходится несколько разныхname, food_year? Кажется, ваш запрос будет неправильным и/или недетерминированным.