web-dev-qa-db-fra.com

MySQL - Soustraction de la valeur de la ligne précédente, groupe par

Je dois avoir la base de la consommation sur la précédente par numéro SN ..__Ce sont mes données:

TABLE EnergyLog

SN     Date                 Value
2380   2012-10-30 00:15:51  21.01
2380   2012-10-31 00:31:03  22.04
2380   2012-11-01 00:16:02  22.65
2380   2012-11-02 00:15:32  23.11
20100  2012-10-30 00:15:38  35.21
20100  2012-10-31 00:15:48  37.07
20100  2012-11-01 00:15:49  38.17
20100  2012-11-02 00:15:19  38.97
20103  2012-10-30 10:27:34  57.98
20103  2012-10-31 12:24:42  60.83

C'est le résultat dont j'ai besoin:

SN      Date                 Value  consumption
2380    2012-10-30 00:15:51  21.01  0
2380    2012-10-31 00:31:03  22.04  1.03
2380    2012-11-01 00:16:02  22.65  0.61
2380    2012-11-02 00:15:32  23.11  0.46
20100   2012-10-30 00:15:38  35.21  0
20100   2012-10-31 00:15:48  37.07  1.86
20100   2012-11-01 00:15:49  38.17  1.1
20100   2012-11-02 00:15:19  38.97  0.8
20103   2012-10-30 10:27:34  57.98  0
20103   2012-10-31 12:24:42  60.83  2.85
30
user1794142

Travailler avec des variables MySQL est très bien, cela ressemble à des assignations de variables de programme en ligne. Tout d’abord, la clause FROM "déclare" les variables @ pour vous, vierges par défaut. Puis interrogez les enregistrements dans l'ordre souhaité. Il effectue un seul passage dans les données au lieu de passer par des sous-requêtes répétées, ce qui peut prendre beaucoup de temps. 

Pour chaque ligne lue, comparez le @lastSN avec le SN de l'enregistrement en cours. Si différent, retournez toujours 0. Si IS est identique, calculez la différence simple. Une fois la comparaison effectuée, définissez @lastSN et @lastValue égaux à ceux de l'enregistrement en cours pour la comparaison des enregistrements suivants.

select
      EL.SN,
      EL.Date,
      EL.Value, --remove duplicate alias
      if( @lastSN = EL.SN, EL.Value - @lastValue, 0000.00 ) as Consumption,
      @lastSN := EL.SN,
      @lastValue := EL.Value
   from
      EnergyLog EL,
      ( select @lastSN := 0,
               @lastValue := 0 ) SQLVars
   order by
      EL.SN,
      EL.Date
68
DRapp

Cela devrait faire l'affaire:

SELECT l.sn,
       l.date, 
       l.value,
       l.value - (SELECT value 
                  FROM energylog x
                  WHERE x.date < l.date
                  AND x.sn = l.sn
                  ORDER BY date DESC
                  LIMIT 1) consumption
FROM energylog l;

Voir SQLFiddle: http://sqlfiddle.com/#!2/b9eb1/8

13
bidifx

Une solution quasi universelle consiste à joindre les données sur elles-mêmes, à retrouver l'enregistrement précédent, en incluant une sous-requête corrélée dans la condition de jointure ...

SELECT
  ThisLog.*,
  COALESCE(ThisLog.Value - PrevLog.Value, 0) AS consumption
FROM
  EnergyLog    AS ThisLog
LEFT JOIN
  EnergyLog    AS PrevLog
    ON  PrevLog.SN   = ThisLog.SN
    AND PrevLog.Date = (SELECT MAX(Date)
                          FROM EnergyLog
                         WHERE SN   = ThisLog.SN
                           AND Date < ThisLog.Date)

Cela fonctionne mieux avec un index couvrant à la fois (SN, Date).

12
MatBailie

Vous pouvez joindre deux lignes de la même table comme ceci: 

    SELECT this.*, prev.*
      FROM tbl this
INNER JOIN tbl prev ON prev.id =
           ( 
               SELECT max(t.id) 
               FROM tbl t  
               WHERE t.id < this.id
           )     
     WHERE ...

Votre cas ressemblera donc à:

    SELECT this.SN, this.Date, this.Value, (this.Value - prev.Value) AS consumption
      FROM EnergyLog this
INNER JOIN EnergyLog prev ON prev.Date =
           ( 
               SELECT max(t.Date) 
               FROM EnergyLog t  
               WHERE t.Date < this.Date
           )     
2
limion

Pouvez-vous s'il vous plaît essayer la requête ci-dessous une fois.

SELECT e1.*,
   (SELECT Value
    FROM EnergyLog e2
    WHERE e2.sn = e1.sn AND e2.date < e1.date
    ORDER BY date DESC
    LIMIT 1)-l.Value consumption
FROM EnergyLog e1;
0
salona