2

通过示例最容易解释。这是我创建触发器的 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,因为任何架构更改都会导致问题。

有没有办法以某种方式遍历每个字段而不是单独检查每个字段?

4

1 回答 1

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 触发器运行后立即将该行添加到历史记录表中,从而跳过任何繁琐的检查。当您在软件中(例如,为了可视化)而不是在数据库中循环它们时,检查行之间的变化。正如我所说,它可能会更好,也可能不会更好,但值得考虑。

于 2013-09-07T02:24:10.387 回答