0

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

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

日期单位  
 ---------- ---------
 2012 年 10 月 10 日 101
 2012 年 11 月 10 日 111
 2012 年 12 月 10 日 121
 13/10/2012 140
 14/10/2012 150
 15/10/2012 155
 16/10/2012 170
 17/10/2012 180
 2012 年 10 月 18 日 185
 2012 年 10 月 19 日 200

所需的输出将是:

日期单位  
 ---------- ---------
 2012 年 10 月 10 日 101
 2012 年 11 月 10 日 10
 2012 年 12 月 10 日 10
 2012 年 10 月 13 日 19
 14/10/2012 10
 2012 年 10 月 15 日 5
 16/10/2012 15
 17/10/2012 10
 2012 年 10 月 18 日 5
 19/10/2012 15

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

4

3 回答 3

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,时间分量设置为常数,例如午夜,并且 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

如果缺少 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
于 2013-07-16T06:56:34.980 回答
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 |
    +----+-------+
于 2013-07-16T07:01:02.047 回答
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
于 2013-07-16T07:03:12.370 回答