如何为 table 创建更新日志?

How to create changelog for table?

我需要在更改某个字段时创建 table 行的更改历史记录。所以我想做的是在 table 更新时创建一个触发器。当字段 txta 更改时,我希望将整行复制到 debug,这是 msser_210 的克隆版本,最后添加了日期时间列,没有数据。我想在更改时添加 NOW() 这样我就有了时间戳。这是我迄今为止尝试过的:

DELIMITER $$
CREATE TRIGGER history_trigger
BEFORE UPDATE ON msser_210
    FOR EACH ROW
        BEGIN
        IF OLD.txta != NEW.txta
        THEN
            INSERT INTO `debug_history` (`idpm`,`posn`,`prnb`,`doid`,`ofcr`,`pidm`,`hitm`,`sitm`,`item`,`dsca`,`igid`,`kitm`,`leng`,`widt`,`hght`,`thik`,`radi`,`quas`,`wght`,`effc`,`colr`,`bdat`,`edat`,`back`,`cuid`,`intb`,`aggr`,`unqu`,`oqua`,`unsq`,`stoc`,`allo`,`hall`,`tqan`,`bqan`,`pkey`,`pric`,`cvqs`,`unsp`,`disc`,`dart`,`ksid`,`anhg`,`txta`,`txti`,`mndn`, `changedate`) VALUES (OLD.idpm,OLD.posn,OLD.prnb,OLD.doid,OLD.ofcr,OLD.pidm,OLD.hitm,OLD.sitm,OLD.item,OLD.dsca,OLD.igid,OLD.kitm,OLD.leng,OLD.widt,OLD.hght,OLD.thik,OLD.radi,OLD.quas,OLD.wght,OLD.effc,OLD.colr,OLD.bdat,OLD.edat,OLD.back,OLD.cuid,OLD.intb,OLD.aggr,OLD.unqu,OLD.oqua,OLD.unsq,OLD.stoc,OLD.allo,OLD.hall,OLD.tqan,OLD.bqan,OLD.pkey,OLD.pric,OLD.cvqs,OLD.unsp,OLD.disc,OLD.dart,OLD.ksid,OLD.anhg,OLD.txta,OLD.txti, OLD.mndn, NOW());
    END IF;
END;
$$

我为什么要这样做是因为我们(可能)有一个带有错误的 php 脚本,该错误将相同的文本字符串写入数据库的每个字段,但我们不知道何时或为什么它既不会发生在它执行的脚本中。有没有更优雅的解决方案?

更新:我在 phpMyAdmin 中找到了 "Track Changes" 的选项,但显然它不会跟踪我们的程序 php-发出的 UPDATE 查询,[=虽然跟踪了 PHP 中的 17=] 和 CREATE TABLE 语句。如果我通过 phpMyAdmin 发出 UPDATE,它会被跟踪。长话短说,我用扳机回到了原来的计划。

更新 2:我自己找到了答案

更新:根据 OP 的评论,显然上下文非常具体。无法访问(或无法反馈和指导开发团队的)代码的基础架构团队需要一种机制来记录生产数据库上的 table 更改。

关于使用触发器的警告:

触发器调试起来可能很棘手,尤其是因为它们是透明的,而且对于新查看您的代码的人来说,触发器在幕后执行某些操作从来都不是显而易见的。 (我是根据经验说的。)它们还会导致 replicated、多主机和集群安装出现问题。 (同样,我是根据经验说的。)此外,如果它们由于某些不相关的原因而失败(例如,它们写入的 table 已损坏),entire 事务 can/will失败 (InnoDB) - 这可能不是您想要的。 (特别是非必要的 "debug" 功能。)

否则,触发器是一个非常有效的工具。 在您的具体情况下,可能是您可用的最佳选择。

您还有其他几个选项,我要强调其中两个:

作为数据访问层的存储过程

如果您非常以数据为中心,并且您已经在数据库中拥有业务逻辑 -(这是一个激烈争论的话题,我在这里不是在争论您应该或不应该在数据库中拥有业务逻辑)然后阅读和通过存储过程写入数据库优势明显

任何事务绑定逻辑都可以插入到这些存储过程中,这样事务不安全调用者(PHP,被一个常见的例子)只需要调用 1 个查询(call sp_insert_tablename(123, 'abc'))并且事务安全可以由数据库强制执行。

可以将临时调试逻辑添加到这些存储过程中,并且 enabled/disabled 通过设置 table 中的标志、会话变量、最终参数,随心所欲。

数据抽象layer/library

类似的原理。为您的客户找到一个数据抽象层(假设您有权更改它的内部结构)。对于 PHP 或 .NET 网络应用程序,有几种流行的选择,所有这些都允许您覆盖(通过代码继承扩展)save/delete 操作以执行您想要的任何其他操作 - 与存储的完全一样程序(但逻辑在客户端模型内部维护)。

如果您想要一个具体的示例,您需要向我们提供有关您正在使用的 stack/language/framework(s) 的更多信息

对于这两个选项,请确保正确处理错误情况。

debug_history 是通过 pypMyAdmin 从原始 table 克隆的。它有一个额外的手动附加的更改列。

ALTER TABLE debug_history ADD COLUMN changedate DATETIME DEFAULT NULL;

我决定是因为没有其他方法我必须自己输入所有的名字。因为我很懒,我得到了最近的 SQL 转储,从用于重建 msser_210 的文件中复制了一个 INSERT INTO-语句并更改了值。

我添加了一个带有自动增量行的额外行,删除了主键并将新的主键设置为新行。

ALTER TABLE debug_history DROP PRIMARY KEY;
ALTER TABLE debug_history ADD COLUMN changenumber INT NOT NULL PRIMARY KEY AUTO_INCREMENT;

我现在有一个可用的更新日志,在 txta 字段中发生更改时触发(请参阅原始格式的触发器问题)。我将 debug_history 中的 txta 列重命名为 txta_old 并创建了一个新列 txta_new.

ALTER TABLE debug_history CHANGE txta txta_old TEXT NOT NULL $$
ALTER TABLE debug_history ADD COLUMN txta_new TEXT NOT NULL AFTER txta_old $$

后来我不得不修改触发器,因为我必须手动复制所有名称..

DROP TRIGGER history_trigger
DELIMITER $$
CREATE TRIGGER history_trigger
BEFORE UPDATE ON msser_210
    FOR EACH ROW
        BEGIN
        IF OLD.txta != NEW.txta
        THEN
            INSERT INTO `debug_history` (`idpm`,`posn`,`prnb`,`doid`,`ofcr`,`pidm`,`hitm`,`sitm`,`item`,`dsca`,`igid`,`kitm`,`leng`,`widt`,`hght`,`thik`,`radi`,`quas`,`wght`,`effc`,`colr`,`bdat`,`edat`,`back`,`cuid`,`intb`,`aggr`,`unqu`,`oqua`,`unsq`,`stoc`,`allo`,`hall`,`tqan`,`bqan`,`pkey`,`pric`,`cvqs`,`unsp`,`disc`,`dart`,`ksid`,`anhg`,`txta_old`,`txta_new`,`txti`,`mndn`, `changedate`) VALUES (OLD.idpm,OLD.posn,OLD.prnb,OLD.doid,OLD.ofcr,OLD.pidm,OLD.hitm,OLD.sitm,OLD.item,OLD.dsca,OLD.igid,OLD.kitm,OLD.leng,OLD.widt,OLD.hght,OLD.thik,OLD.radi,OLD.quas,OLD.wght,OLD.effc,OLD.colr,OLD.bdat,OLD.edat,OLD.back,OLD.cuid,OLD.intb,OLD.aggr,OLD.unqu,OLD.oqua,OLD.unsq,OLD.stoc,OLD.allo,OLD.hall,OLD.tqan,OLD.bqan,OLD.pkey,OLD.pric,OLD.cvqs,OLD.unsp,OLD.disc,OLD.dart,OLD.ksid,OLD.anhg,OLD.txta,NEW.txta,OLD.txti, OLD.mndn, NOW());
        END IF;
    END;
$$