如何将具有相同ID的新的和最后插入的条目相加并在新条目中插入结果

时间:2015-05-25 21:26:27

标签: php mysql

我试图使用PHP和MYSQL自动计算平均燃料消耗量。但我不知道该怎么做。这是解释:

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

*数据作为一个例子我想要的样子。一切都有效,除了AVERAGE colum,这就是我在这里的原因。

我试图制作php函数,它会为新的和最后一个KM条目的每个新条目加上相同的CAREID(例如CARID 9):

  • 新条目KM 174857 - 上次条目KM 174114 = 743
  • 新条目LI 70.17(对于CARID 9),此金额为70.17 /(743/100)
  • 将结果插入新条目AVERAGE

我花了很多时间试图让这个工作,但我只是从未接近过。

5 个答案:

答案 0 :(得分:5)

方法

您的方法中有两个错误,这会带来复杂性。

  1. 任何可以派生的列,例如AVERAGE都应存储。

    如果存储它,它将构成一个重复的列...这会导致更新异常,正如您所遇到的那样。规范化的目的是消除数据重复,从而消除更新异常。它还消除了诸如此类的复杂代码以及触发器等。

    动态计算结果集 中的SUM(),AVG()等。

  2. 使用ID列,这基本上意味着您有一个记录归档系统,没有关系数据库。没有列举它导致的许多问题(我已经在其他地方做过),只是在这里命名问题

    • 你有一个ID心态。

    ID是物理记录指针,它不提供关系数据库所需的行唯一性。

    ID是物理记录指针,它没有任何意义,用户不应该看到它。但是你(和其他人)已经赋予它意义。

    它将您粘贴到文件的物理结构,而不是数据的逻辑结构。这反过来会使您的代码变得复杂。

    因此,如果没有给出正确的CREATE TABLE命令,请保留原样,让我们​​假装文件中不存在ID和AVERAGE。

  3. 第三项,与进近无关,似乎从给出的数字,10.58,你想要每升公里,而你详细的算术(每100公里的升数)将产生9.44。如果你想要某种平均值,你最好先弄清楚这些元素。

    解决方案

        (Code obsolete due to revision)
    

    修订问题

    我试图获得你给出的数字,而问题仍然困惑(请注意相关的评论)。既然您有Revised个问题,那么现在的要求就很明确了。现在看起来你想要(a)每100公里的升数[仍然不是"平均值"],以及(b)每个记录的总体数字[一种运行总计]。在这种情况下,请使用此代码。

    上述说明仍然有效且适用。

        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
    

    注意我正在操纵数据,只有数据,没有物理字段,我们不应该关心文件的物理方面。不存储每100公里的升数(您称之为AVERAGE),并且避免更新异常。每个记录的总体数字仅在显示时间#34;即时显示。

    这也会消除您的/first entry问题。

    当然,CARID对用户来说也毫无意义。

    请随时发表评论或提问等。

    硬存储

    存储可以导出的值存在许多问题。这是数据存储级别的硬编码。当然,你可以使用触发器来缓解疼痛,但它仍然无法工作,因为(a)原则被打破,(b)它违反了现有的工程原则。例如。当单行的LI输入错误(例如700.17),并随后更正(例如70.17)时会发生什么?该汽车的所有后续行现在都不正确,必须重新计算和更新。所以现在你需要一个Update触发器和一个Insert触发器。癌症化合物本身。

    更新异常的概念,即禁止存储可以导出的值,自1970年以来一直伴随着我们,这是有充分理由的。我们完全有理由避免它们。

答案 1 :(得分:2)

在我看来,执行此操作的适当方法是使用BEFORE INSERT触发器。这样的触发器可能如下所示:

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 ;

每次为该车插入新条目时,这将自动平均每辆车的最后两个条目。

demo here

答案 2 :(得分:1)

以下查询获取每辆车的最后一个ID:

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;

接下来,对于您想要的信息,您可以加入表格以获取完整记录,然后进行计算:

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;

答案 3 :(得分:1)

我还是要发帖。 我的想法是:

  • 使用数组返回使用最后2行(新条目和最后一个条目)。 因此我可以使用count(array) - 1&amp; -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();
 }
?>

结果:

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

我做了数学 - 这是正确的70.17 / 7.43 = 9.44

答案 4 :(得分:0)

AVERAGE CARID=5&amp; CARID=8CARID=9的计算结果不一样,所以我的示例并不完全匹配,但如果您在插入上尝试此操作,则可以执行类似

的操作
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示例 - http://sqlfiddle.com/#!9/dce1d/1