按日期获取单位消耗量

时间:2013-07-16 06:38:48

标签: mysql

我正在努力以下列方式从mysql获取结果。我在mysql db表中有10条记录,有日期和单位字段。我需要在每个日期都使用单位。

表结构如下,在每个记录中添加今天单位和过去的前一单位:

 Date        Units  
 ----------  ---------
 10/10/2012   101
 11/10/2012   111
 12/10/2012   121
 13/10/2012   140
 14/10/2012   150
 15/10/2012   155
 16/10/2012   170
 17/10/2012   180
 18/10/2012   185
 19/10/2012   200

所需的输出将是:

 Date        Units  
 ----------  ---------
 10/10/2012   101
 11/10/2012   10
 12/10/2012   10
 13/10/2012   19
 14/10/2012   10
 15/10/2012   5
 16/10/2012   15
 17/10/2012   10
 18/10/2012   5
 19/10/2012   15

任何帮助将不胜感激。谢谢

3 个答案:

答案 0 :(得分:2)

有几种方法可以获得结果集。如果您可以在结果集中使用额外的列以及列的顺序,那么这样的事情是可行的方法。


使用用户变量

SELECT d.Date
     , IF(@prev_units IS NULL
         ,@diff := 0
         ,@diff := d.units - @prev_units
       ) AS `Units_used`
     , @prev_units := d.units AS `Units`
  FROM ( SELECT @prev_units := NULL ) i
  JOIN ( 
         SELECT t.Date, t.Units
           FROM mytable t
          ORDER BY t.Date, t.Units
       ) d

这将返回指定的结果集,但它也包含Units列。可以将该列过滤掉,但它更昂贵,因为MySQL处理内联视图的方式(MySQL称之为“派生表”)

要删除该额外列,您可以将其包装在另一个查询...

SELECT f.Date
     , f.Units_used
  FROM (
         query from above goes here
       ) f
 ORDER BY f.Date

但同样,删除该列会带来第二次实现该结果集的额外成本。


使用半联接

如果保证每个Date值都有一行,可以存储为DATE,也可以存储为DATETIME,并将timecomponent设置为常量,例如午夜,并且Date值中没有间隙,和Date定义为DATE或DATETIME数据类型,然后另一个将返回特定结果集的查询:

SELECT t.Date
     , t.Units - s.Units AS Units_Used
  FROM mytable t
  LEFT
  JOIN mytable s
    ON s.Date = t.Date + INTERVAL -1 DAY
  ORDER BY t.Date

如果缺少日期值(间隙),使得前一行没有匹配,则Units_used将具有NULL值。


使用相关子查询

如果您没有“缺少日期”的保证,但您保证特定日期的行数不超过一行,则另一种方法(通常在性能方面更昂贵)是使用相关子查询:

SELECT t.Date
     , ( t.Units - (SELECT s.Units 
                      FROM mytable s
                     WHERE s.Date < t.Date
                     ORDER BY s.Date DESC
                     LIMIT 1) 
       ) AS Units_used
  FROM mytable t
 ORDER BY t.Date, t.Units

答案 1 :(得分:1)

spencer7593的解决方案会更快,但你也可以这样做......

SELECT * FROM rolling;
+----+-------+
| id | units |
+----+-------+
|  1 |   101 |
|  2 |   111 |
|  3 |   121 |
|  4 |   140 |
|  5 |   150 |
|  6 |   155 |
|  7 |   170 |
|  8 |   180 |
|  9 |   185 |
| 10 |   200 |
+----+-------+

SELECT a.id,COALESCE(a.units - b.units,a.units) units
  FROM 
 ( SELECT x.*
        , COUNT(*) rank
     FROM rolling x
 JOIN rolling y
       ON y.id <= x.id
GROUP
   BY x.id
 ) a     
LEFT
JOIN 
 ( SELECT x.*
        , COUNT(*) rank
     FROM rolling x
 JOIN rolling y
       ON y.id <= x.id
GROUP
   BY x.id
 ) b
ON b.rank= a.rank -1;

    +----+-------+
    | id | units |
    +----+-------+
    |  1 |   101 |
    |  2 |    10 |
    |  3 |    10 |
    |  4 |    19 |
    |  5 |    10 |
    |  6 |     5 |
    |  7 |    15 |
    |  8 |    10 |
    |  9 |     5 |
    | 10 |    15 |
    +----+-------+

答案 2 :(得分:0)

这应该会产生预期的结果。我不知道你的表是如何被调用的所以我把它命名为“tbltest”。 命名表日期通常是一个坏主意,因为它也引用其他东西(函数,数据类型......)所以我将其重命名为“fdate”。在字段名称或表名中使用大写字符也是一个坏主意,因为它使您的语句更少与数据库无关(某些数据库区分大小写,有些则不区分)。

SELECT
  A.fdate,
  A.units - coalesce(B.units, 0) AS units
FROM
  tbltest A left join tbltest B ON A.fdate = B.fdate + INTERVAL 1 DAY