使用静态 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,您需要相应地调整 <>s。
使用动态 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。
如果这些假设不成立,您需要进行调整。如果您需要这方面的帮助,请告诉我。