How to sum new and last inserted record with same id and insert result into new record

I am trying to make an automatic sum of average fuel consumption using PHP and MYSQL. But I don't know how to do it. This explanation:

Tabe CONSUM:

ID   CARID   LI        KM          DATETIME         AVERAGE
--------------------------------------------------------------
6     9     70.17   174857   2015-02-14 12:58:51      9.44
5     5     51.00   154785   2015-02-13 10:11:19      8.73
4     8     99.44   485627   2015-02-12 11:45:48      6.84
3     9     47.78   174114   2015-02-11 10:21:32  /first entry
2     8     24.74   484175   2015-02-10 10:28:37  /first entry
1     5     89.65   154201   2015-02-09 10:01:14  /first entry

      

* Data as an example of how I want to look. Everything works except AVERAGE colum, so I am here.

I am trying to make a php function that will add the sum for every new record of a new and last KM record with the same CAREID, something like this (example CARID 9):

  • New record KM 174857 - Last record KM 174114 = 743
  • New entry LI 70.17 (for CARID 9), in this amount 70.17 / (743/100)
  • insert the result as New AVERAGE record .

I spent a lot of time trying to get this to work, but I just never got even close.

+3


source to share


5 answers


It seems to me that the appropriate way to do this is with a trigger BEFORE INSERT

. Such a trigger might look like this:

delimiter //
create trigger avg_calc before insert on consum 
  for each row 
  begin
    declare lastOdo int;       -- variable to hold the last odometer reading
    select km
      into lastOdo             -- store the last reading here
      from consum
      where carid = NEW.carid  -- for the carid we are inserting
      order by `datetime` desc -- get the last one by date
      limit 1;
    set NEW.average = (NEW.km - lastOdo) / NEW.li;  -- update the average we're about to insert
  end//
delimiter ;

      



This will automatically average the last two records per vehicle each time a new record is inserted for that vehicle.

demo here

+2


source


An approach

You have two mistakes in your approach that entail complexity.

  • Any column that can be dumped like your AVERAGE should not be stored .

    If stored, it is a repeating column ... which causes the anomaly to be updated as you experience. The normalization point is to eliminate data duplication and thus eliminate update anomalies. It also eliminates complex code like this one as well as triggers, etc.

    Calculate SUM (), AVG (), etc. in the result set only , on the fly.

  • Using identity columns, which basically means you have a record-writing system, no relational database. Without listing many of the problems it causes (I did it elsewhere), just named the problem here

    • You have a way of thinking.

    An identifier is a pointer to a physical record, it does not provide row uniqueness as required for relational databases.

    The ID is a pointer to the physical record, it doesn't mean anything, the user shouldn't see it. But you (and others) gave it meaning.

    You tie the glue to the physical structure of the file, not the logical data structure. This, in turn, complicates your code.

    So without giving you the corrected command CREATE TABLE

    , leaving yours as they are, let's pretend the ID and AVERAGE don't exist in the file.

The third element, unrelated to the approach, it seems that from Figure 10.58 you want to use kilometers per liter, whereas the arithmetic you detailed (liters per 100 km) would be 9.44. If you really want something in between, you better understand the elements.

Decision

    (Code obsolete due to revision)

      

Revised question

I was trying to get the numbers you gave while the question remained confusing (note the comments on this question). Since you have Revised your question, this requirement now becomes clear. Now it looks like you want (a) liters per 100 km [still not "average"], and (b) a total for each entry [sort of a grand total]. In this case, use this code.

The above note remains valid and applicable.



    SELECT  CARID,
            DATETIME,
            KM,
            LI,
            LPCK = ( LI_TOT / ( ( KM_LAST-KM_FIRST / 100 ) )  -- not stored
        FROM (
            -- create a Derived Table with KM_FIRST
            SELECT  CARID,
                    DATETIME,
                    -- not stored
                    KM_FIRST = (
                SELECT  MIN( KM )        -- get the first KM for car
                    FROM CONSUM
                    WHERE CARID = C.CARID
                    ),
                    KM_LAST = (
                SELECT  MAX( KM )        -- get the last KM for car
                    FROM CONSUM
                    WHERE CARID = C.CARID
                    ),
                    KM,                  -- KM for this row
                    LI,                  -- LI for this row
                    LI_TOT = (
                SELECT  SUM( LI )        -- get the total LI for car
                    FROM CONSUM
                    WHERE CARID = C.CARID
                    AND KM != (          -- exclude first LI for car
                    SELECT  MIN( KM )    -- get the first KM for car
                        FROM CONSUM
                        WHERE CARID = C.CARID
                        )
                    )
                FROM CONSUM C
            ) AS CONSUM_EXT

        ORDER BY CARID,
            DATETIME

      

Note that I am manipulating data, only data, no physical fields, we don't need to care about the physical aspects of the file. Liters per 100 km (what you call AVERAGE) is not saved and there is an update anomaly prevented. The total score for each record is calculated on the fly only during display.

This also fixes the problem /first entry

.

Of course it CARID

also doesn't make sense to the user.

Please feel free to comment or ask questions, etc.

Hard storage

There are many problems with storing the value that can be retrieved. This is hard coding at the storage layer. Of course, you can use a trigger to ease the pain, but it still won't work because (a) the principle is violated and (b) it violates existing engineering principles. For example. what happens when the LI for one line is entered incorrectly (eg 700.17) and subsequently corrected (eg 70.17)? All subsequent lines for this car are now incorrect and must be recalculated and updated. So now you need an Update trigger as well as an Insert trigger. Cancer compounds themselves.

The concept of "renewal anomaly", the prohibition on the storage of valuables that can be obtained, has been with us since 1970 for a good reason. We avoid them for a good reason.

+5


source


The following request gets the last ID for each vehicle:

select c.*,
       (select c2.id
        from consum c2
        where c2.carid = c.carid and c2.id < c.id
        order by c2.id desc
        limit 1
       ) as last_id
from consum c;

      

Next, for the information you want, you can join the table to get the complete record and then do the calculation:

select c.ID, c.CARID, c.LI, c.KM, c.DATETIME,
       c.li / (c.km - cprev.km) / 100) as avg
from (select c.*,
             (select c2.id
              from consum c2
              where c2.carid = c.carid and c2.id < c.id
              order by c2.id desc
              limit 1
             ) as last_id
      from consum c
     ) c left join
     consum cprev
     on c.last_id = cprev.id;

      

+1


source


I'll post anyway. My idea:

  • Using the last two lines (new record and last record) using array return. So I could use count (array) - 1 and -2.

...

<?php 

include("./inc.connect.php");

$Query =  "SELECT id, km, li
            FROM consum
            WHERE cardid = 9";


$users = $db->query($Query);

$array_res = $users->fetchAll();
$nb_rows = count($array_res);

$diff_km = $array_res[($nb_rows - 1)]['km'] - $array_res[($nb_rows - 2)]['km'];

$new_li = number_format(($array_res[($nb_rows - 1)]['li'] / ($diff_km * 0.01)),2);

print "<pre>";
print_r($array_res);
print "</pre>";

echo "diff km : " . $diff_km . " new_li : " . $new_li . "<br>";

$UpdateQuery = "UPDATE consum SET average = '$new_li' WHERE id = " .     
                 $array_res[($nb_rows - 1)]['id'];

/* Begin a transaction, turning off autocommit */
try 
{  
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $db->beginTransaction();

   $sth = $db->exec($UpdateQuery);
   $db->commit(); 
} 
catch (Exception $e) 
{
   $db->rollBack();
   echo "Failed: " . $e->getMessage();
 }
?>

      

Result:

Array
(
[0] => Array
    (
        [id] => 3
        [0] => 3
        [km] => 174114
        [1] => 174114
        [li] => 47.78
        [2] => 47.78
    )

[1] => Array
    (
        [id] => 6
        [0] => 6
        [km] => 174857
        [1] => 174857
        [li] => 70.17
        [2] => 70.17
    )

)

diff km : 743 new_li : 9.44

UPDATE consum SET average = '9.44' WHERE id = 6

      

I did the math - this is correct 70.17 / 7.43 = 9.44

+1


source


Your AVERAGE

where CARID=5

and CARID=8

does not compute the same as where where CARID=9

, so my example does not match exactly, but if you are trying to do it on insert, you can do something like

INSERT INTO CONSUM
SELECT 
    6,
    9,
    70.17,
    174857,
    '2015-02-14 12:58:51', 
    ROUND((174857-a.KM)/70.17, 2) 
FROM CONSUM a
WHERE a.CARID = 9 
ORDER BY ID DESC 
LIMIT 1;

      

Sqlfiddle example - http://sqlfiddle.com/#!9/dce1d/1

0


source







All Articles