通过示例最容易解释。这是我创建触发器的MySQL查询:
DELIMITER $$
DROP TRIGGER IF EXISTS `tblExample_UPDATE`$$
CREATE TRIGGER `tblExample_UPDATE` AFTER UPDATE ON `tblExample`
FOR EACH ROW
BEGIN
IF (NEW.field1 != OLD.field1) THEN
INSERT INTO tblChanges (object_type,object_id,change_type,field_name,field_before,field_after,log_time) VALUES ("example",NEW.id,"edited","field1",OLD.field1,NEW.field1,NOW());
END IF;
IF (NEW.field2 != OLD.field2) THEN
INSERT INTO tblChanges (object_type,object_id,change_type,field_name,field_before,field_after,log_time) VALUES ("example",NEW.id,"edited","field2",OLD.field2,NEW.field2,NOW());
END IF;
IF (NEW.field3 != OLD.field3) THEN
INSERT INTO tblChanges (object_type,object_id,change_type,field_name,field_before,field_after,log_time) VALUES ("example",NEW.id,"edited","field3",OLD.field3,NEW.field3,NOW());
END IF;
.
..
...
..
.
IF (NEW.field1337 != OLD.field1337) THEN
INSERT INTO tblChanges (object_type,object_id,change_type,field_name,field_before,field_after,log_time) VALUES ("example",NEW.id,"edited","field1337",OLD.field1337,NEW.field1337,NOW());
END IF;
END$$
DELIMITER ;
基本上我正在逐字段检查更改。它确实有效,但是要维护PITA,因为任何架构更改都会导致问题。
有没有办法以某种方式循环遍历每个字段而不是单独检查每个字段?
答案 0 :(得分:1)
虽然我不确定,但我认为你想要做的事情在MySQL中是不可能直接实现的。不过我确实知道一种解决方法。
您可以从information_schema.columns
获取列的列表:
SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = 'tblExample' AND TABLE_SCHEMA = DATABASE()
您可以使用此列列表使用脚本自动生成触发器。如果您使用的是PHP,它看起来有点像(未经测试的)示例代码:
$db = new PDO('mysql:dbname=information_schema;host=localhost', 'user', 'pass');
$stmt = $db->prepare('SELECT COLUMN_NAME FROM COLUMNS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?');
$stmt->bindValue(1, $myTableName);
$stmt->bindValue(1, $myDatabaseName);
$stmt->execute();
$triggerSql = '';
while (($columnName = $stmt->fetchColumn()) !== false) {
$triggerSql .= 'IF (OLD.' . quoteIdentifier($columnName) . ' <> NEW.' . quoteIdentifier($columnName) . ') THEN
INSERT INTO tblChanges(object_type, object_id, change_type, field_name, field_before, field_after, log_time) VALUES (\'example\', NEW.id, \'edited\', ' . $db->quote($columnName) . ', OLD.' . quoteIdentifier($columnName) . ', NEW.' . quoteIdentifier($columnName) . ', CURRENT_TIMESTAMP);
END IF;';
}
echo 'CREATE TRIGGER ' . quoteIdentifier($myTriggerName) . ' AFTER UPDATE ON ' . quoteIdentifier($myTableName) . '
FOR EACH ROW
BEGIN
' . $triggerSql . '
END;';
请注意,可能(或可能不)更好地记录到每个表的“历史记录表” 而不是所有表的一个巨大的“历史记录表”。在这种情况下,您可以创建每个表的副本,只需添加额外的ID和时间戳列进行跟踪。这种方法有其自身的缺点,但在语义上稍微更正确。在这种方法中,您可以考虑在AFTER UPDATE触发器运行后立即将行添加到历史记录表中,跳过任何繁琐的检查。当您在软件中循环(例如,用于可视化)而不是在数据库中时,检查行之间的更改。正如我所说,它可能会或可能不会更好,但值得考虑。