I have a table of driver speeds and road segments:
driver_lpr | segment | speed
0000001 | A | 30
0000002 | B | 60
0000003 | A | 50
0000004 | A | 100
0000005 | B | 60
And I want to have a table of average speed per segment
segment | average speed
A | 47.368
B | 60
How can this be done in SQL ?
When averaging speeds, the harmonic mean is in need.
The straight forward AVG()
approach is wrong, the arithmetic mean yields the wrong result for average velocity.
There is no predefined function for the harmonic mean, but it could be achieved with this query:
SELECT segment,
COUNT(*)/SUM(1e0/speed) AS avg_speed
FROM T
GROUP BY segment
This is little bit complex, but will take care of 0
speed also. I have 2 similar queries to do it based on different scenarios.
Assume your source table like below.
+-------------+----------+-------+
| driver_lpr | segment | speed |
+-------------+----------+-------+
| 0000001 | A | 30 |
| 0000002 | B | 60 |
| 0000003 | A | 50 |
| 0000004 | A | 100 |
| 0000005 | B | 60 |
| 0000006 | B | 0 |
| 0000007 | C | 0 |
+-------------+----------+-------+
I have added 2 new rows with 0
speed.
Case 1:
- Addition of a
0
speed in segmentB
, will give average speed as40
(60*2+0*1)/(2+1)
. - Addition of a new segment,
C
with0
speed will give average speed as0
So output would be
+----------+-------------------+
| segment | average_speed |
+----------+-------------------+
| A | 47.36842105263158 |
| B | 40 |
| C | 0 |
+----------+-------------------+
Case 2:
- There will be no change in average speed of
B
with addition of a0
. - However, a new segment
C
will have0
average speed.
Output would be
+----------+-------------------+
| segment | average_speed |
+----------+-------------------+
| A | 47.36842105263158 |
| B | 60 |
| C | 0 |
+----------+-------------------+
Query for Case 1:
with tbl1 as
(SELECT segment,
case when speed = 0 then cast(0 as float) else
cast(1 as float)/cast(speed as float)
end as newspeed
FROM T
),
tbl2 as
(
select segment,cast(1 as float)/avg(newspeed) as avgspeed,count(*) as cnt
from tbl1
where newspeed <> 0
group by segment
union
select segment,0 as avgspeed,count(*) as cnt
from tbl1
where newspeed =0
group by segment
)
select segment,
sum(avgspeed*cnt)/sum(cnt) as "average_speed"
from tbl2
group by segment
Query for Case2
with tbl1 as
(SELECT segment,
case when speed = 0 then cast(0 as float) else
cast(1 as float)/cast(speed as float)
end as newspeed
FROM T
),
tbl2 as
(
select segment,cast(1 as float)/avg(newspeed) as avgspeed,count(*) as cnt
from tbl1
where newspeed <> 0
group by segment
union
select segment,0 as avgspeed,count(*) as cnt
from tbl1
where newspeed =0
group by segment
)
select segment,
sum(avgspeed) as "average_speed"
from tbl2
group by segment
来源:https://stackoverflow.com/questions/34794664/how-should-i-calculate-the-average-speed-by-road-segment-for-multiple-segments