有没有办法在MySQL触发器中遍历所有字段而不是重复自己?

时间:2013-09-07 00:43:38

标签: mysql

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

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

1 个答案:

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