Home > Blockchain >  Calculate sum when value changes
Calculate sum when value changes

Time:08-22

I am trying to build a system that will track vehicle fuelings, and have run into a problem with one report; determining fuel efficiency in distance/fuel. Sample data is:

odometer fuel partial_fillup
61290 10.3370 0
61542 6.4300 0
61735 4.3600 0
61994 7.5000 0
62242 5.4070 0
62452 8.1100 0
62713 5.7410 1
62876 9.4850 0
63243 6.1370 1
63499 10.7660 0

Where odometer is the total distance the vehicle has traveled, fuel is the number of gallons or liters put in, and partial_fillup is a boolean meaning the fuel tank was not completely filled if non-zero.

If the user fills the tank each time the query I can use is:

set @a = null;
select 
   odometer,
   odometer-previousOdometer distance,
   fuel,
   (odometer-previousOdometer)/fuel mpg,
   partial_fillup
from
   (
      select 
         @a as previousOdometer,
         @a:=odometer,
         odometer,
         fuel/1000 fuel,
         partial_fillup
      from fuel
      where 
         vehicle_id =1
         and odometer >= 61290
      order by odometer
   ) as readings
where readings.previousOdometer is not null;

However, when the user only partially fills the tank, the correct procedure would be to subtract the last full fueling from current odometer reading, then divide by the sum of all fuel since the previous odometer reading, so at odometer 63499, the calculate would be (63499-62876)/(10.7660 6.1370)

CodePudding user response:

This will get the average used on the last ride:

select
  odometer,
  odometer-lag(odometer) over (order by odometer) as distance,
  fuel,
  (odometer-lag(odometer) over (order by odometer))/fuel as mpg
from fuel

output:

odometer distance fuel mpg
61290 10.3370
61542 252 6.4300 39.1913
61735 193 4.3600 44.2661
61994 259 7.5000 34.5333
62242 248 5.4070 45.8665
62452 210 8.1100 25.8940
62713 261 5.7410 45.4625
62876 163 9.4850 17.1850
63243 367 6.1370 59.8012
63499 256 10.7660 23.7786

Or you can calculate the total drive distance, and the total amount of fuel used:

select
   distance,
   sum_fuel,
   distance/sum_fuel as mpg
from (
select
   f.odometer,
   f.odometer-(select min(odometer) from fuel) as distance,
   fuel,
   sum_fuel
from fuel f
inner join (
   select
      odometer,
      sum(fuel) over (order by R) as sum_fuel
   from (
      select 
         odometer, 
         fuel,
         row_number() over (order by odometer) R
      from fuel) x
   ) x on x.odometer = f.odometer
) x2

which will get next output, which will get closer to an average after a longer time of measurement:

distance sum_fuel mpg
0 10.3370 0.0000
252 16.7670 15.0295
445 21.1270 21.0631
704 28.6270 24.5922
952 34.0340 27.9720
1162 42.1440 27.5721
1423 47.8850 29.7170
1586 57.3700 27.6451
1953 63.5070 30.7525
2209 74.2730 29.7416

DBFIDDLE

CodePudding user response:

I was able to figure it out after studying Luuk's answer. I'm sure there is a more efficient way to do this; I am not used to using variables in SQL. But, the answers are correct in the test data.

set @oldOdometer = null;
set @totalFuel = 0;

select
   s.odometer,
   format(fuel, 3) fuel,
   s.distance,
   format( distance / fuel, 2) as mpg
from (
   select
      partial_fillup as partial,
      odometer,
      (fuel @totalFuel) as fuel,
      @totalFuel as totalFuel,
      @oldOdometer oldOdometer,
      if ( partial_fillup, null,odometer - @oldOdometer ) as distance,
      @totalFuel := if ( partial_fillup, @totalFuel   fuel, 0) as pastFuel,
      @oldOdometer := if (partial_fillup,@oldOdometer,odometer ) as runningOdometer
   from
      fuel
   order by
      odometer ) s
where s.distance is not null
order by s.odometer
limit 1,999;

limit 1,999 simply there to skip the first row returned, since there is not enough data to calculate distance or mpg. On my copy of MySQL, doing this means you do not need to initialize the two variables (you don't have to include the set commands at the beginning), so it works with my reporting tool very well. If you do initialize them, you do not need the limit statement. Works assuming you don't have more than 999 rows returned.

  • Related