【问题标题】:Get Average value for each X rows in SQL获取 SQL 中每 X 行的平均值
【发布时间】:2014-02-08 05:26:29
【问题描述】:

假设我有下表

+----+-------+
| Id | Value |
+----+-------+
|  1 |   2.0 |
|  2 |   8.0 |
|  3 |   3.0 |
|  4 |   9.0 |
|  5 |   1.0 |
|  6 |   4.0 |
|  7 |   2.5 |
|  8 |   6.5 |
+----+-------+

我想绘制这些值,但由于我的真实表有数千个值,我考虑获取每 X 行的平均值。我有什么办法可以这样做,即每 2 或 4 行,如下所示:

2
+-----+------+
| 1-2 |  5.0 |
| 3-4 |  6.0 |
| 5-6 |  2.5 |
| 7-8 |  4.5 |
+-----+------+

4
+-----+------+
| 1-4 |  5.5 |
| 5-8 |  3.5 |
+-----+------+

另外,有没有办法根据我的表中的总行数使这个 X 值动态化?例如,如果我有 1000 行,则将根据每 200 行 (1000/5) 计算平均值,但如果我有 20 行,则根据每 4 行 (20/5) 计算平均值。

我知道如何以编程方式执行此操作,但有没有使用纯 SQL 的方法?

编辑:我需要它在 mysql 上工作。

【问题讨论】:

  • 什么数据库——oracle,MySQL,什么?
  • MySQL。问题已更新。

标签: mysql sql


【解决方案1】:

取决于您的 DBMS,这样的事情会起作用:

SELECT
   ChunkStart = Min(Id),
   ChunkEnd = Max(Id),
   Value = Avg(Value)
FROM
   (
      SELECT
         Chunk = NTILE(5) OVER (ORDER BY Id),
         *
      FROM
         YourTable
   ) AS T
GROUP BY
   Chunk
ORDER BY 
   ChunkStart;

根据您的要求,无论有多少行,这都会创建 5 个组或块。

如果你没有窗口函数,你可以伪造它:

SELECT
   ChunkStart = Min(Id),
   ChunkEnd = Max(Id),
   Value = Avg(Value)
FROM
   YourTable
GROUP BY
   (Id - 1) / (((SELECT Count(*) FROM YourTable) + 4) / 5)
;

我在这里做了一些假设,例如Id 以 1 开头并且没有间隙,如果事情没有平均分配,你会希望最后一组太小而不是太大。我还假设整数除法会导致在 Ms SQL Server 中。

【讨论】:

  • 它在 sqlite3 上完美运行,但在 mysql 中却没有按预期运行:link。我忘了提到我需要它在 mysql 上工作。
  • 我设法将最后一行更改为Floor((Id - 1) / Floor(((SELECT Count(*) FROM YourTable) + 4) / 5))。谢谢!
  • 没有间隙的假设仅适用于“假冒”版本
  • 供参考 - NTILE:将有序分区中的行分布到指定数量的组中。这些组从一个开始编号。对于每一行,NTILE 返回该行所属组的编号。 msdn.microsoft.com/en-us/library/ms175126.aspx
  • 添加ORDER BY ChunkStart 很重要,否则块将/可能出现乱序
【解决方案2】:

您可以使用模运算符对表格的每第 N 行进行操作。此示例将获取每 10 行的平均值:

select avg(Value) from some_table where id % 10 = 0;

然后您可以计算表中的行数,对其应用一些因素,并将该值用作动态间隔:

select avg(Value) from some_table where id % (select round(count(*)/1000) from some_table) = 0;

当然,您需要根据表格中的实际行数找出最佳间隔。

编辑: 重读您的帖子,我意识到这是每 N 行的平均值,而不是每个连续的 N 行的平均值。我不确定这是否足够,或者您是否特别需要连续平均值。

【讨论】:

  • 我需要这些值来绘制一个小折线图,所以这些值需要是连续的。只获得第 N 个值会让我已经创建了一些东西,但我害怕忽略一些值。
  • 如何在循环中使用它,我可以获得前 10 行的平均值,但之后它会重复相同的结果。
【解决方案3】:

查看 NTILE 函数(如四分位数、五分位数、十分位数、百分位数)。您可以使用它将数据平均分成多个存储桶 - 在您的情况下,您似乎想要五个。

然后您可以使用 AVG 计算每个桶的平均值。

NTILE 在 SQL-99 中,所以大多数 DBMS 都应该有它。

【讨论】:

  • 抱歉,我需要它在不支持 NTILE 的 MySQL 上工作。
【解决方案4】:

你可以试试

CREATE TABLE #YourTable
(
    ID int
    ,[Value] float
)
INSERT #YourTable (ID, [Value]) VALUES
 (1,2.0)
,(2,8.0)
,(3,3.0)
,(4,9.0)
,(5,1.0)
,(6,4.0)
,(7,2.5)
,(8,6.5)


SELECT
    ID = MIN(ID) + '-' + MAX(ID)
    ,[Value] = AVG([Value])
FROM
(
    SELECT  
        GRP = ((ROW_NUMBER() OVER(ORDER BY ID) -1) / 2) + 1
        ,ID = CONVERT(VARCHAR(10), ID)
        ,[Value]
    FROM
        #YourTable
) GrpTable
GROUP BY 
    GRP

DROP TABLE #YourTable

【讨论】:

  • 欢迎来到 SO,弗朗索瓦·莫林!此处不鼓励仅使用代码的答案,因为它们无法深入了解问题是如何解决的。请更新您的解决方案,说明您的代码如何解决手头的问题:)
猜你喜欢
  • 2019-03-24
  • 2014-04-23
  • 2021-12-05
  • 1970-01-01
  • 2023-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多