mysql - 选择值和值已更改的列

标签 mysql sql mariadb

我有一个历史记录,每当 1 个值发生变化时,它就会记录 1 行。所以在每一行中应该总是只有 1 个值从前一行发生了变化

我的 table 是这样的。 (我有超过 5 个列并且它们没有通用名称,这些列名称将针对每个项目更改。我想避免硬编码列名称)

INDEX|Column1|Column2|Column3|Column4|Column5|TimeStamp 
1    |      0|      0|      0|      0|      0|2017-03-28 13:00:00
2    |      0|      0|      0|      1|      0|2017-03-28 14:00:00
3    |      2|      0|      0|      1|      0|2017-03-28 15:00:00
4    |      3|      0|      0|      1|      0|2017-03-28 16:00:00
5    |      3|     22|      0|      1|      0|2017-03-28 17:00:00
6    |      3|     22|      6|      1|      0|2017-03-28 18:00:00

我想在我的报告中得到的结果。

Name   |NewValue|PreviousValue|TimeStamp
Column4|       1|            0|2017-03-28 14:00:00
Column1|       2|            0|2017-03-28 15:00:00
Column1|       3|            2|2017-03-28 16:00:00
Column2|      22|            0|2017-03-28 17:00:00
Column3|       6|            0|2017-03-28 18:00:00

实现此目标的最佳方法是什么?

我正在使用 MariaDB 10.1.22。

最佳答案

使用静态 SQL:

这是您想要的基本代码。但是一切都是硬编码的。如果没有“动态 SQL”,这是无法避免的。

SELECT
CASE
    WHEN t1.Column1 <> t2.Column1 THEN 'Column1'
    WHEN t1.Column2 <> t2.Column2 THEN 'Column2'    
    WHEN t1.Column3 <> t2.Column3 THEN 'Column3'
    WHEN t1.Column4 <> t2.Column4 THEN 'Column4'
    WHEN t1.Column5 <> t2.Column5 THEN 'Column5'
END as Name,
CASE
    WHEN t1.Column1 <> t2.Column1 THEN t2.Column1
    WHEN t1.Column2 <> t2.Column2 THEN t2.Column2   
    WHEN t1.Column3 <> t2.Column3 THEN t2.Column3
    WHEN t1.Column4 <> t2.Column4 THEN t2.Column4
    WHEN t1.Column5 <> t2.Column5 THEN t2.Column5
END as NewValue,
CASE
    WHEN t1.Column1 <> t2.Column1 THEN t1.Column1
    WHEN t1.Column2 <> t2.Column2 THEN t1.Column2   
    WHEN t1.Column3 <> t2.Column3 THEN t1.Column3
    WHEN t1.Column4 <> t2.Column4 THEN t1.Column4
    WHEN t1.Column5 <> t2.Column5 THEN t1.Column5
END as PreviousValue,
t2.TimeStamp
FROM
MyTable t1
JOIN MyTable t2
    ON t2.Index = t1.Index + 1

如果列可以是 NULL无论是之前还是之后,您都需要调整 <>相应地。

使用动态 SQL:

您可以通过从 information_schema 中提取列数据来生成类似上面的代码,而不是对列名称进行硬编码。表。设置 @tablename变量为您要处理的表的名称,此代码将设置变量 @sql持有等同于上述的代码。我用过 \n添加新行,从而改进生成代码的格式。

-- Avoids the GROUP_CONCAT truncating the code we're building
SET SESSION GROUP_CONCAT_MAX_LEN=65535;

-- Specify your target table here
SELECT @tablename := 'MyTable';

SELECT @sql := GROUP_CONCAT(theCode SEPARATOR '')
FROM
(
-- theOrderBy is for keeping the various parts of this code in the correct sequence
-- SELECT plus the opening of the first CASE statement
SELECT 0 theOrderBy, 'SELECT\nCASE\n' theCode

UNION ALL

-- Generates the inner part first CASE statement
SELECT 1, CONCAT('    WHEN t1.',Column_Name,' <> t2.',Column_Name,' THEN ''', Column_Name,'''','\n')
FROM `information_schema`.`Columns`
WHERE table_name like @tablename
AND Column_Name NOT IN ('Index','Timestamp')

UNION ALL

-- Generates the end of the first CASE (and it's alias) and the beginning of the second one
SELECT 2, 'END as Name,\nCASE\n'

UNION ALL

-- Generates the inner part second CASE statement
SELECT 3, CONCAT('    WHEN t1.',Column_Name,' <> t2.',Column_Name,' THEN t2.', Column_Name, '\n')
from `information_schema`.`Columns`
WHERE table_name LIKE @tablename
AND Column_Name NOT IN ('Index','Timestamp')

UNION ALL

-- Generates the end of the second CASE (and it's alias) and the beginning of the third one    
SELECT 4, 'END as NewValue,\nCASE\n'

UNION ALL

-- Generates the inner part third CASE statement
SELECT 5, CONCAT('    WHEN t1.',Column_Name,' <> t2.',Column_Name,' THEN t1.', Column_Name, '\n')
FROM `information_schema`.`Columns`
WHERE table_name like @tablename
AND Column_Name NOT IN ('Index','Timestamp')

UNION ALL

-- Generates the end of the final CASE statement plus the FROM clause
SELECT 6, CONCAT('END as PreviousValue,\nt2.Timestamp\nFROM\n    ',@tablename, ' t1\n    JOIN ',@tablename, ' t2 ON\n        t2.Index = t1.Index + 1')

ORDER BY theOrderBy
) as sub;

SELECT @sql;

如果您对在变量中看到的内容感到满意,您可以重新运行并替换最后一行:

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

此代码的可行性在很大程度上取决于您对任何目标表具有相同的基本结构,即顺序增加的 Index场和一个Timestamp .与“静态”代码一样,这也使用了 <>对于所有比较,如此有效地假设字段不会是 NULL在每次更改之前或之后。

如果这些假设不成立,您将需要进行调整。如果您需要这方面的帮助,请告诉我。

关于mysql - 选择值和值已更改的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44376361/

相关文章:

php - 使用 PHP 确保添加到 MySQL 表的唯一性

Mysql:如果join中存在特定值,则获取其他列的最小值

php - 如何更改 "Many to Many"关系中的变量?

php - 使 sql 子查询停止提示重复列

mysql - 如何在Mysql中生成排行榜?

php - 在 SQL 查询中使用带计数的 DISTINCT 关键字

laravel - MySQL View 不会在 Laravel 5.5 迁移中下降

mysql - 使用具有连接的 MySQL 子查询时的命名冲突

mysql - 使用过程和触发器更新表

Mysqli 从给定值在任何列中的行中获取多个不同的值