在保留表中的所有数据的同时基于不同的 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 yearDISTINCT 的组合进行汇总。 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上保留重复项

前端:使用与基于 id 的不同表相关的表中的数据

SQL Server:在保留保留NULL结果的同时,对联接表中的一个字段进行分组

如果字段的值为空,则在 MySQL 中插入新记录,同时在不同的字段中添加 ID+1

基于链接三个表中的数据更新项目

如何在codeigniter中查看基于链接ID的所有数据