为每一行选择一个值已更改的列

  • 本文关键字:选择 一行 一个 mysql sql
  • 更新时间 :
  • 英文 :


我试图从MySQL表中提取数据,并以一种日志文件格式插入到另一个表中。

比方说我有这张桌子。

表A

+--------+----+-------+-------+-------+---------+
|UniqueID|Item|ColumnA|ColumnB|ColumnC|TimeStamp|
+--------+----+-------+-------+-------+---------+
|1       | 1  | 500   | 600   | 700   |   13:01 |
|2       | 2  |  50   |  60   |  70   |   13:03 |
|3       | 3  |  17   |  18   |  19   |   13:12 |
|4       | 1  | 501   | 600   | 700   |   13:15 |
|5       | 1  | 501   | 600   | 699   |   13:18 |
|6       | 3  |  20   |  18   |  19   |   13:22 |
|7       | 1  | 501   | 600   | 702   |   13:25 |
|8       | 2  |  50   |  66   |  70   |   13:26 |
|9       | 3  |  20   |  25   |  19   |   13:32 |
+--------+----+-------+-------+-------+---------+

我有多张表,里面有不同数量的列和项目。我不介意对表名和列名进行硬编码。

我想最后得到这张表

+----+------+-------+--------+-------------+---------+
|Item|Table |Column |NewValue|PreviousValue|TimeStamp|
+----+------+-------+--------+-------------+---------+
| 1  |TableA|ColumnA| 501    | 500         | 13:15   |
| 1  |TableA|ColumnC| 699    | 700         | 13:18   |
| 3  |TableA|ColumnA|  20    |  17         | 13:22   |
| 1  |TableA|ColumnC| 699    | 702         | 13:25   |
| 2  |TableA|ColumnB|  66    |  60         | 13:26   |
| 3  |TableA|ColumnB|  25    |  18         | 13:32   |
+----+------+-------+--------+-------------+---------+

MySQL并没有让这一切变得非常容易。您可以通过计算上一个时间戳并将其加入来获得上一条记录。在MySQL 8.0中,您可以使用lag()来实现此目的。

然后您需要取消对这些值的透视。MySQL并没有让这变得非常容易,但您可以使用cross joincase表达式。剩下的只是条件逻辑和过滤:

select item, 'TableA' as tableName, colname,
col as newvalue, col_prev as prevvalue, timestamp
from (select t.*, c.colname,
(case when c.colname = 'columnA' then columnA
when c.colname = 'columnB' then columnB
when c.colname = 'columnC' then columnC
end) as col,
(case when c.colname = 'columnA' then tprev.columnA
when c.colname = 'columnB' then tprev.columnB
when c.colname = 'columnC' then tprev.columnC
end) as col_prev
from (select t.*,
(select max(t2.timestamp)
from tablea t2
where t2.item = t.item and t2.timestamp < t.timestamp
) as timestamp_prev
from tablea t
) t join
t tprev
on t.item = tprev.item and t.timestamp_prev = tprev.timestamp cross join
(select 'columnA' as colname union all
select 'columnB' as colname union all
select 'columnC' as colname
) c
) ct
where col_prev <> col;

最新更新