Douglas Vanny
Douglas Vanny

Reputation: 139

Get Average value for each X rows in SQL

Let`s say I have the following table

+----+-------+
| 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 |
+----+-------+

I want to plot these values, but since my real table has thousands of values, I thought about getting and average for each X rows. Is there any way for me to do so for, ie, each 2 or 4 rows, like below:

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 |
+-----+------+

Also, is there any way to make this X value dynamic, based on the total number of rows in my table? Something like, if I have 1000 rows, the average will be calculated based on each 200 rows (1000/5), but if I have 20, calculate it based on each 4 rows (20/5).

I know how to do that programmatically, but is there any way to do so using pure SQL?

EDIT: I need it to work on mysql.

Upvotes: 3

Views: 9279

Answers (4)

Francois Morin
Francois Morin

Reputation: 1

You can try that

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

Upvotes: 0

ErikE
ErikE

Reputation: 50271

Depending on your DBMS, something like this will work:

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;

This creates 5 groups or chunks no matter how many rows there are, as you requested.

If you have no windowing functions you can fake it:

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

I made some assumptions here such as Id beginning with 1 and there being no gaps, and that you would want the last group too small instead of too big if things didn't divide evenly. I also assumed integer division would result as in Ms SQL Server.

Upvotes: 7

Concrete Gannet
Concrete Gannet

Reputation: 580

Look at the NTILE function (as in quartile, quintile, decile, percentile). You can use it to split your data evenly into a number of buckets - in your case it seems you would like five.

Then you can use AVG to calculate an average for each bucket.

NTILE is in SQL-99 so most DBMSes should have it.

Upvotes: 1

Miro
Miro

Reputation: 5337

You can use modulos operator to act on every Nth row of the table. This example would get the average value for every 10th row:

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

You could then do a count of the rows in the table, apply some factor to that, and use that value as a dynamic interval:

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

You'll need to figure out the best interval based on the actual number of rows you have in the table of course.

EDIT: Rereading you post I realize this is getting an average of every Nth row, and not each sequential N rows. I'm not sure if this would suffice, or if you specifically need sequential averages.

Upvotes: 2

Related Questions