将列设置为等于同一表中另一列的滞后值

时间:2015-06-04 16:26:40

标签: mysql sql

我有一个记录日期的列,我想将另一列设置为日期列的滞后版本。换句话说,对于每个日期,我希望新列具有上一个日期。 我尝试了很多东西,大多是傻瓜,我无处可去。我的主要问题是我根据同一个表和同一列的where子句更新了一个列,并且MySQL不允许它。

以下是数据的示例。我的目标是更新colum PREVDATE,使用DATA_DATE中的上一行,条件是两行的GVKEY相同。我将如下定义前一行,按GVKEY和DATE_DATE ASC排序,并为每一行(假设GVKEY是相同的)我想要前一行

+--------------+--------+---------+-------+----------+-------------+
| DATA_DATE    |PREVDATE|  PRICE  | GVKEY | CUR_DEBT |  LT_DEBT    |
+--------------+--------+---------+-------+----------+-------------+
| 1965-05-31   |   NULL | -17.625 | 1004  | 0.198    | 1.63        |
| 1970-05-31   |   NULL | -18.375 | 1004  | 2.298    | 1.58        |
+--------------+--------+---------+-------+----------+-------------+

1 个答案:

答案 0 :(得分:2)

这是一种利用MySQL用户定义变量的方法,以及无法保证的行为,但看起来是一致的(至少在MySQL 5.1,5.5和5.6中)。

警告:这将返回表中的每个行。您可能需要考虑对有限范围的gvkey值执行此操作以进行测试。添加WHERE子句......

         SELECT IF(r.gvkey=@prev_gvkey,@prev_ddate,NULL) AS prev_date
              , @prev_gvkey := r.gvkey                   AS gvkey
              , @prev_ddate := r.data_date               AS data_date
           FROM (SELECT @prev_ddate := NULL, @prev_gvkey := NULL) i
          CROSS
           JOIN mytable r
         ORDER BY r.gvkey, r.data_date

SELECT列表中表达式的顺序很重要,我们需要在保存@prev_变量中的当前值之前,将当前行的值与前一行的“已保存”值进行比较。下一行。

我们需要一个条件测试来确保我们仍在使用相同的gvkey。 gvkey的第一个data_date不会有“previous”data_date,所以我们需要返回一个NULL。

为了获得最佳效果,我们希望有一个覆盖索引,其中gvkeydata_date为主要列:

   ... ON mytable (gvkey,data_data)

索引可以包含其他列,但是我们首先需要这两列,按顺序排列。这将允许MySQL使用索引“按顺序”返回行,并避免昂贵的“使用filesort”操作。 (EXPLAIN的额外列将显示MySQL“使用索引”。)

一旦我们正确运行,我们就可以将其用作UPDATE语句中的内联视图。

例如:

UPDATE mytable t
  JOIN (
         SELECT IF(r.gvkey=@prev_gvkey,@prev_ddate,NULL) AS prev_date
             , @prev_gvkey := r.gvkey                    AS gvkey 
             , @prev_ddate := r.data_date                AS data_date
          FROM (SELECT @prev_ddate := NULL, @prev_gvkey := NULL) i 
         CROSS
          JOIN mytable r
         ORDER BY r.gvkey, r.data_date
       ) s
    ON t.gvkey     = s.gvkey
   AND t.data_date = s.data_date
   SET t.prev_date = s.prev_date

(同样,对于一个非常大的表,我们可能希望通过在内联视图中包含gvkey的谓词来将该事务分解为更小的块,以限制返回/更新的行数。)

批量gvkey范围内这样做是合理的方法......例如。

/* first batch */    WHERE r.gvkey >=    1 AND r.gvkey <  100
/* second run  */    WHERE r.gvkey >=  100 AND r.gvkey <  200
/* third batch */    WHERE r.gvkey >=  200 AND r.gvkey <  300

显然,还有其他方法/ SQL模式可以实现相同的结果。我用这种方法取得了成功。

要强调早期的重要注意:这取决于无法保证的行为,以及MySQL参考手册警告的行为(使用这样的用户定义变量)。 )