提问人:Andrew G. Johnson 提问时间:9/7/2013 更新时间:9/7/2013 访问量:1466
有没有办法遍历MySQL触发器中的所有字段,而不是重复自己?
Is there a way to loop through all fields in a MySQL trigger rather than repeating yourself?
问:
最容易通过示例来解释。这是我用于创建触发器的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赞
Tomas Creemers
9/7/2013
#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 触发器运行时立即将该行添加到历史记录表中,跳过任何繁琐的检查。当您在软件中循环访问行(例如,用于可视化)而不是在数据库中循环行时,请检查行之间的更改。正如我所说,它可能会更好,也可能不会更好,但值得考虑。
评论