Mysql 如何为表创建变更日志?

Mysql 如何为表创建变更日志?,mysql,debugging,changelog,Mysql,Debugging,Changelog,当某个字段发生更改时,我需要创建表行的更改历史记录。所以我想做的是在表更新时创建一个触发器。当字段txta更改时,我希望整行复制到debug,这是msser_210的克隆版本,末尾添加了一列datetime,没有数据。我想在更改时添加NOW(),这样我就有了时间戳。这就是我迄今为止所尝试的: DELIMITER $$ CREATE TRIGGER history_trigger BEFORE UPDATE ON msser_210 FOR EACH ROW BEGIN

当某个字段发生更改时,我需要创建表行的更改历史记录。所以我想做的是在表更新时创建一个触发器。当字段
txta
更改时,我希望整行复制到
debug
,这是
msser_210
的克隆版本,末尾添加了一列datetime,没有数据。我想在更改时添加
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脚本(可能)有一个bug,它将相同的文本字符串写入数据库的每个字段,但我们不知道它发生的时间或原因,也不知道它是哪个脚本。有没有更优雅的解决方案

更新:我在phpMyAdmin中找到了“跟踪更改”的选项,但显然它没有跟踪我们的程序php发出的
UPDATE
查询,
DROP
CREATE TABLE
语句。如果我通过phpMyAdmin发布一个
更新
,它会被跟踪。长话短说,我带着扳机回到了原来的计划


更新2:我自己找到了答案更新:根据OP的评论,显然上下文非常具体。如果基础架构团队无法访问(或无法反馈和指导开发团队的)代码,则需要一种机制来记录生产数据库中的表更改

关于使用触发器的警告

触发器可能很难调试,尤其是因为它们是透明的,而且对于刚看过您的代码的人来说,触发器在幕后执行某些操作从来都不明显。(我是根据经验说的。)它们还可能导致多主机和群集安装出现问题。(同样,我是根据经验说的。)此外,如果它们由于一些不相关的原因(例如,它们写入的表被破坏)而失败,那么整个事务可以/将失败(InnoDB)——这可能不是您想要的。(尤其是非必要的“调试”功能。)

否则,触发器是一个非常有效的工具在您的特定场景中,可能是您可用的最佳选择。

您还可以选择其他几个选项,我将重点介绍其中两个选项:

存储过程作为数据访问层

如果您非常以数据为中心,并且在数据库中已经有了业务逻辑(这是一个备受争议的话题,我不是在这里争论您应该或者不应该在数据库中有业务逻辑),那么通过存储过程对数据库进行读写具有明显的优势

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

可以将临时调试逻辑添加到这些存储过程中,并通过设置表中的标志、会话变量、最终参数等启用/禁用临时调试逻辑

数据抽象层/库

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

如果你想要一个具体的例子,你需要给我们更多关于你使用的堆栈/语言/框架的信息


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

调试历史记录是通过pypMyAdmin从原始表中克隆的。它手动追加了一个额外的changedate列

ALTER TABLE debug_history ADD COLUMN changedate DATETIME DEFAULT NULL;
我决定这样做是因为没有其他办法,我必须自己输入所有的名字。因为我懒惰,我得到了一个最近的SQL转储,从用于重建
msser\u 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;
$$

Tbh的程序是一个失败的事业,我不是它的开发人员,我不能代码php。我是服务器系统管理员,我被告知将所有写入给定数据库的内容都记录下来,这样开发人员就可以调试(甚至没有单元测试等),这样做很好,但我对此无能为力。这是绝对好的。然后我建议我的答案仍然能回答你的问题——“否则,触发器是一个非常有效的工具。”坚持你提出的解决方案,那就好了。我对触发器的体验与你所处的场景完全相同——把触发器放在那里的人被迫这么做。作为一个更广泛话题的一部分,如果我处在你的位置,我会举手(你是否有正式的基础设施接口开发流程,比如devops?)并说“我被迫这么做,这很好,但它会咬到我们,我希望记录在案”。我试过了,但是新表中的列数不同,因为我为changedate添加了一列,并希望设置它。这是跨表复制数据时的常见问题。您可以通过显式地命名字段的子集来限制复制的字段,自动地这样做(如您的示例中所示)将