Mysql 将列设置为同一表中另一列的滞后值
我有一个记录日期的列,我想将另一列设置为日期列的滞后版本。换句话说,对于每个日期,我都希望新列具有上一个日期。 我尝试了很多东西,大部分都是愚蠢的,结果一无所获。我的主要问题是,我根据同一个表和同一个列中的where子句更新了一个列,MySQL不允许这样做 下面是一个数据示例。我的目标是使用DATA_DATE中的前一行更新columprevdate,条件是两行的GVKEY相同。我将按如下方式定义前一行,按GVKEY和DATE_DATE ASC排序,对于每一行,如果GVKEY相同,我想要前一行Mysql 将列设置为同一表中另一列的滞后值,mysql,sql,Mysql,Sql,我有一个记录日期的列,我想将另一列设置为日期列的滞后版本。换句话说,对于每个日期,我都希望新列具有上一个日期。 我尝试了很多东西,大部分都是愚蠢的,结果一无所获。我的主要问题是,我根据同一个表和同一个列中的where子句更新了一个列,MySQL不允许这样做 下面是一个数据示例。我的目标是使用DATA_DATE中的前一行更新columprevdate,条件是两行的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 |
+--------------+--------+---------+-------+----------+-------------+
这里有一种方法可以使用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
选择列表中表达式的顺序很重要,我们需要将当前行的值与前一行保存的值进行比较,然后再将当前值保存在@prev_uuu变量中,以用于下一行
我们需要一个条件测试来确保我们仍然在使用同一个gvkey。gvkey的第一个数据_日期不会有以前的数据_日期,因此我们需要返回NULL
为了获得最佳性能,我们需要一个覆盖索引,以gvkey和data_date作为前导列:
... ON mytable (gvkey,data_data)
索引可以在这些列之后包含其他列,但我们需要按顺序首先包含这两列。这将允许MySQL使用索引按顺序返回行,并避免使用filesort操作带来的开销。解释中的额外列将显示使用索引的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参考手册警告不要使用这样的用户定义变量。我想您需要一个连接更新。MySQL不允许您在子查询中更新同一个表。有多少行可能具有给定的GVKEY?从订购角度定义上一个日期的定义可能有数千个。我会定义前一行如下,按GVKEY和DATE_DATE ASC排序,对于我想要的每一行,前一行和DATA_DATE是Datetime,而不是hometype@DrewPierce这是日期格式的。谢谢@spencer7593,效果非常好。我花了一段时间才弄明白,但解释也很棒!为了得到下一个日期,我只需要按降序排序,对吗?另外,我的桌子不是太大,所以性能根本不是问题。我花了不到一分钟的时间。是的,你也可以做同样的事情来获得下一个日期,只需取消订单。为了确保可以有效地使用可用索引,请按降序执行整个操作。。。按gvkey DESC、data_date DESC订购。如果您想存储前一行的价格,您可以使用相同的模式从前一行获取任何列。显然,在以后插入或删除行时,prev_date列不会自动维护。