在保留表中的所有数据的同时基于不同的 ID 计算平均值?
Posted
技术标签:
【中文标题】在保留表中的所有数据的同时基于不同的 ID 计算平均值?【英文标题】:Calculating average based on distinct ID while preserving all the data in a table? 【发布时间】:2021-10-13 00:46:29 【问题描述】:如果我有这样的数据:
+------+----+-------+-------+
| year | id | value | group |
+------+----+-------+-------+
| 2019 | 1 | 10 | A |
| 2019 | 1 | 10 | B |
| 2019 | 2 | 20 | A |
| 2019 | 3 | 30 | A |
| 2019 | 2 | 20 | B |
| 2020 | 1 | 5 | A |
| 2020 | 1 | 5 | B |
| 2020 | 2 | 10 | A |
| 2020 | 3 | 15 | A |
| 2020 | 2 | 10 | B |
+------+----+-------+-------+
有没有办法在保留所有数据的同时根据不同的id
计算平均value
?
我需要这样做,因为我还将有WHERE
子句来过滤表中的其他列,但我还需要在WHERE
子句的情况下获得数据的整体视图) 未添加(这些 WHERE 过滤器将由我无法控制的 OUTERMOST 查询中的自动化软件添加)。
group
列就是一个例子。
对于上面的例子,结果应该是:
Overall --> 20 for 2019 and 10 for 2020
WHERE group = 'A'
--> 2019 年为 20 个,2020 年为 10 个
WHERE group = 'B'
--> 2019 年为 15,2020 年为 7.5
我尝试执行以下操作:
SELECT
year,
AVG(IF(id = LAG(id) OVER (ORDER BY id), NULL, value)) AS avg
FROM table
WHERE group = 'A' -- this clause may or may not exist
GROUP BY year
基本上我在想,如果我按 id 排序并检查前一行以查看它是否具有相同的 id,则该值应为NULL
,因此它不会计入计算中,但不幸的是我不能t 将分析函数放入 aggregate
函数中。
【问题讨论】:
窗口函数是在HAVING之后应用的,所以你的代码是不合法的。为显示的样本数据提供所需的输出。此外 - 您的数据包含每个(年份,id)对的相同值 - 它是绝对的吗? 同一个 id 在不同组别和年份的值是否总是相同的,比如 id =1 两个组别在两个年份的值都是 10? 你的 mysql 版本是多少? 对于相同的 id 和年份,值始终相同。唯一的区别是组。我有最新的 mysql 版本 使用GROUP BY year
,您将获得两个结果行,一个用于 2019 年,一个用于 2020 年。根据添加或未添加的 WHERE
子句,您将显示更大或更小的平均值。在这种情况下,“同时保留所有数据”是什么意思?我不明白。应该保留什么。结果只有两列,年份和平均值,对吧?或者你想要不同的结果。那么请在您的请求中表明这一点。
【参考方案1】:
虽然数据模型不合适且未规范化(您正在冗余存储值),但真正的问题是后期自动化 SQL 注入(可选添加的 where 子句)。
当 where 子句添加到您的查询时,一切都很好,因为 where 子句正确地限制了要考虑的行(组 A 或 B)。但是,当没有添加 where 子句时,您将不得不处理聚合数据集(不同的年份/id 行)。后者意味着聚合上的聚合,可以使用子查询来完成,如 DineshDB 在较早的答案中所示。但是这里的问题是 where 子句必须对中间结果(子查询)起作用,并且您说您的软件将 where 子句添加到主查询中。
令人惊讶的解决方案是进行这三个聚合。在下面的查询中,我混合了MAX
(第一个聚合)、AVG OVER
(第二个聚合)和DISTINCT
(第三个聚合),这三个可以愉快地共存于一个查询中。不需要子查询。
SELECT DISTINCT
year,
AVG(MAX(value)) OVER (PARTITION BY year)
FROM yourtable
WHERE `group` = ... -- optional where clause
GROUP BY year, id
ORDER BY year;
演示:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=973ae4f260597392c55f260d3c260084
【讨论】:
这很好并回答了问题,但我想知道是否有办法通过仅按年份分组来做到这一点 您需要先按年份和 id 分组,因为存在冗余值。只有这样,您才能按年份汇总,我们使用PARTITION BY year
和 DISTINCT
的组合进行汇总。 PARTITION BY year
是按年份分组,只是它没有减少行数,为此我们需要额外的DISTINCT
。【参考方案2】:
以下查询将为您提供预期的输出。
SELECT
`Year`,
AVG(DISTINCT `value`*1.0) `value`
FROM table
WHERE `group` = 'B' -- this clause is optional
GROUP BY `Year`;
查询将返回以下结果。
Year | Value
2019 | 20
2020 | 10
SQLFiddle
【讨论】:
where 子句需要在外部查询中。自动化软件无法访问子查询。 如果不同的 ID 具有相同的值会怎样?我认为平均计算不正确。另外乘以1.0有什么意义?以上是关于在保留表中的所有数据的同时基于不同的 ID 计算平均值?的主要内容,如果未能解决你的问题,请参考以下文章
删除左表上的重复项,同时在右表SELECT JOIN上保留重复项
SQL Server:在保留保留NULL结果的同时,对联接表中的一个字段进行分组