mysql 日志记录触发器,查找更改的列
Posted
技术标签:
【中文标题】mysql 日志记录触发器,查找更改的列【英文标题】:mysql Trigger for logging, find changed columns 【发布时间】:2012-07-24 06:39:51 【问题描述】:我正在编写一个触发器来跟踪表中发生的所有更改。不幸的是,该表有 150 多列,我想避免在代码中编写每一列(例如 new.col1、new.col2 ....),因此我在“更新触发器后”中编写了以下查询
INSERT INTO logs SELECT *, NOW() FROM abc WHERE abc.id = NEW.Id;
由于更新查询中未更改的数据重复,此想法导致了多个问题。
简而言之,我想动态找出哪些列是更新查询的一部分,如果不可能,有没有办法遍历“新”行的所有列,以便我可以动态比较 old.@colName == new.@colName?
我已经看过了 Oracle PL/SQL: Loop Over Trigger Columns Dynamically、How to determine if anything changed in update trigger in t-sql 和 mysql UPDATE trigger: INSERTing the values of the columns that actually changed。
最后一个链接是我所需要的,只有一个区别,我不想在下面的语句中硬编码列名,因为我要编写类似触发器的所有表中有超过 100 多个列为了!!
IF NEW.column1 <> OLD.column1 THEN INSERT INTO... END IF; IF NEW.column2 <> OLD.column2 THEN INSERT INTO... END IF
【问题讨论】:
【参考方案1】:我今天早上对此进行了一些研究,看起来我遇到了很多与您相同的搜索结果。最终,在我看来,没有办法遍历所有表列并引用相应的旧/新值。我正在明确检查每一列,然后记录:
IF (NEW.fld1 <> OLD.fld1) OR (NEW.fld1 IS NOT NULL AND OLD.fld1 IS NULL) OR (NEW.fld1 IS NULL AND OLD.fld1 IS NOT NULL) THEN
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`)
VALUES ("tblname", "fld1", OLD.fld1, NEW.fld1);
END IF;
IF (NEW.fld2 <> OLD.fld2) OR (NEW.fld2 IS NOT NULL AND OLD.fld2 IS NULL) OR (NEW.fld2 IS NULL AND OLD.fld2 IS NOT NULL) THEN
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`)
VALUES ("tblname", "fld2", OLD.fld2, NEW.fld2);
END IF; ...
我发现了另一个解决方案here 的暗示。理论上,您可以有 3 个分隔列表,一个用于列名,一个用于旧 val,一个用于新 val。您必须明确引用旧的和新的 val,但这将是一行(更易于维护或复制/粘贴以在其他表上实现),然后您可以循环。所以在伪代码中它看起来像这样:
fields_array = concat_ws(",", "fld1", "fld2");
old_vals_array = concat_ws(",", OLD.fld1, OLD.fld2);
new_vals_array = concat_ws(",", NEW.fld1, NEW.fld2);
foreach fields_array as key => field_name
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`)
VALUES ("tblname", field_name, old_vals_array[key], vew_vals_array[key]);
我没有想太多。您可能需要调用存储过程而不是设置变量。但这可能值得研究。我已经在触发器上花费了足够的时间。不确定我是否可以(向我的老板)验证更优雅的解决方案的试错时间。
【讨论】:
我现在遇到了一种新问题...即使我准备好在触发器中对所有 150 列进行硬编码,我也很难跟踪“UpdatedBy”列!我的原始表跟踪登录到应用程序的人以及谁通过应用程序进行了最后一次更改。不幸的是,我们的支持技术可能会直接从数据库更改数据而不更新“updatedBy”列,在这种情况下,我无法知道“updatedBy”是否是查询的一部分,因此我无法设置该列需要时为空!我希望我在这里有意义:) 有道理。不过对此也无能为力。这就是 API 和“接口”的用途。强迫“支持”人员/其他开发人员做他们应该做的事情。【参考方案2】:正如 ingratiatednerd 已经建议的那样,您可以使用 CONCAT_WS 从所有必需的值中创建字符串并创建一个比较语句。
也许以下内容对某人有用:
DECLARE old_concat, new_concat text;
SET old_concat = CONCAT_WS(',', OLD.fld1, OLD.fld2, ...);
SET new_concat = CONCAT_WS(',', NEW.fld1, NEW.fld2, ...);
IF old_concat <> new_concat
THEN
INSERT STATEMENT
END IF;
【讨论】:
以上是关于mysql 日志记录触发器,查找更改的列的主要内容,如果未能解决你的问题,请参考以下文章