2012-07-24 43 views
5

我正在写一个触发器来跟踪表中发生的所有更改。不幸的是,该表有150列,我想避免写代码中的每一列(例如new.col1,new.col2 ....),因此我在“更新触发器后”中写下了以下查询:mysql触发记录,找到更改列

INSERT INTO logs SELECT *, NOW() FROM abc WHERE abc.id = NEW.Id; 

此想法由于在更新查询中没有更改的数据的重复而导致多个问题。

简而言之,我想动态地找出哪些列是更新查询的一部分,如果这是不可能的,有没有一种方法可以遍历“新”行的所有列,以便我可以动态比较旧。@ colName == new。@ colName?

我已经看到了 Oracle PL/SQL: Loop Over Trigger Columns Dynamically,How to determine if anything changed in update trigger in t-sqlMySQL 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 

回答

2

今天早上我一直在做这方面的一些研究,看起来像我遇到过很多和你一样的搜索结果。最终,在我看来,无法遍历所有表列,并引用相应的旧/新值。我安定在明确检查每个列,然后登录:

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个分隔列表,一个用于列名,一个用于旧的vals,另一个用于新的vals。你将不得不明确引用新的和新的vals,但这将是一行(更容易维护或复制/粘贴到其他表上实现),然后你可以循环。因此,在伪代码中,它看起来像这样:

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]); 

我没有想到这一点通过太多。您可能需要调用存储过程而不是设置变量。但它可能值得深入研究。我已经花了足够的时间在我的触发器上。不确定我可以通过更优雅的解决方案验证(对我的老板)试错时间。

+0

我一直正面临着一个新的问题。即使我已经准备好硬编码我所有的150列在我的触发器,我有很难跟踪“UpdatedBy”列!我的原始表跟踪登录到应用程序的人员以及通过应用程序进行最后更改的人员。不幸的是,我们的支持技术可能会直接从数据库更改数据,而不更新“updatedBy”列,在这种情况下,我无法知道“updatedBy”是否是查询的一部分,因此我无法设置该列当需要时为null!我希望我在这里有意义:) – Sap 2012-08-17 05:06:16

+0

有道理。尽管如此,却无能为力。这是API和“接口”的用途。强迫“支持”人员/其他开发人员做他们应该做的事情。 – Jegsar 2015-02-20 04:55:55

1

正如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;