查找 MySQL 触发器中哪些列已更改
Find which columns have changed in MySQL triggers
我正在处理遗留应用程序的审计要求。遗留应用程序是在 Java 中使用 JDBC 编写的,底层数据库在 MySQL 中。我需要用给定 table 的任何更改填充审计 table,审计 table DDL 是这样的
CREATE TABLE ENTITY_AUD (
id INT AUTO_INCREMENT PRIMARY KEY,
baseTableId INT FK,
beforeValue INT,
afterValue INT,
changedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
changedBy VARCHAR2
);
我必须通过触发器来执行此操作,因为我们希望避免对遗留 Java 代码进行任何更改。我知道什么时候可以通过使用旧变量和新变量知道给定列的旧值和新值,就像这样
DELIMITER $$
CREATE TRIGGER ENTITY_AFTER_UPDATE
AFTER UPDATE
ON Entity FOR EACH ROW
BEGIN
IF OLD.quantity <> new.quantity THEN
INSERT INTO ENTITY_AUD(baseTableId,beforeValue, afterValue,changedBy)
VALUES(old.id, old.quantity, new.quantity,new.updated_by);
END IF;
END$$
DELIMITER ;
我如何才能动态找到所有已更改的列并为每个列更新填充新行?我不想要每列的 if 语句,源 tables 可能有 40 列。
最好使用像 Debezium 这样的 CDC 工具来代替触发器。
您可以通过查询 INFORMATION_SCHEMA.COLUMNS
获得属于您的 Entity
table 的列列表,但这没有帮助。无法在基于列的触发器中生成动态语句,因为 MySQL 不支持 运行 在触发器中动态 SQL。
如果您必须使用触发器,那么您只需 hard-code 所有 40 列。
您可以通过使用 MySQL 8.0 功能中的新功能将 WHERE 子句添加到没有 table 引用的 SELECT 中来稍微简化一下。如果 WHERE 子句为假,则 SELECT returns 零行。然后您可以将它们与 UNION 串在一起以形成一组行,每一行对应更改的每一列。
BEGIN
INSERT INTO ENTITY_AUD (baseTableId, beforeValue, afterValue, changedBy)
SELECT OLD.id, OLD.quantity, NEW.quantity, NEW.updated_by
WHERE OLD.quantity <> NEW.quantity
UNION
SELECT OLD.id, OLD.price, NEW.price, NEW.updated_by
WHERE OLD.price <> NEW.price
UNION
SELECT OLD.id, OLD.billing, NEW.billing, NEW.updated_by
WHERE OLD.billing <> NEW.billing
UNION
...37 others...
END
这个问题过去曾多次出现。这是我找到的一些,但毫无疑问还有更多。
- mysql Trigger for logging, find changed columns (2012)
- MySQL update trigger - find changed columns? (2013)
- Mysql triggers - capture each column change (2014)
- MYSQL update Trigger check changes in all columns and insert values to other table (2019)
- MySQL Trigger compare all columns in OLD and NEW (2021)
我正在处理遗留应用程序的审计要求。遗留应用程序是在 Java 中使用 JDBC 编写的,底层数据库在 MySQL 中。我需要用给定 table 的任何更改填充审计 table,审计 table DDL 是这样的
CREATE TABLE ENTITY_AUD (
id INT AUTO_INCREMENT PRIMARY KEY,
baseTableId INT FK,
beforeValue INT,
afterValue INT,
changedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
changedBy VARCHAR2
);
我必须通过触发器来执行此操作,因为我们希望避免对遗留 Java 代码进行任何更改。我知道什么时候可以通过使用旧变量和新变量知道给定列的旧值和新值,就像这样
DELIMITER $$
CREATE TRIGGER ENTITY_AFTER_UPDATE
AFTER UPDATE
ON Entity FOR EACH ROW
BEGIN
IF OLD.quantity <> new.quantity THEN
INSERT INTO ENTITY_AUD(baseTableId,beforeValue, afterValue,changedBy)
VALUES(old.id, old.quantity, new.quantity,new.updated_by);
END IF;
END$$
DELIMITER ;
我如何才能动态找到所有已更改的列并为每个列更新填充新行?我不想要每列的 if 语句,源 tables 可能有 40 列。
最好使用像 Debezium 这样的 CDC 工具来代替触发器。
您可以通过查询 INFORMATION_SCHEMA.COLUMNS
获得属于您的 Entity
table 的列列表,但这没有帮助。无法在基于列的触发器中生成动态语句,因为 MySQL 不支持 运行 在触发器中动态 SQL。
如果您必须使用触发器,那么您只需 hard-code 所有 40 列。
您可以通过使用 MySQL 8.0 功能中的新功能将 WHERE 子句添加到没有 table 引用的 SELECT 中来稍微简化一下。如果 WHERE 子句为假,则 SELECT returns 零行。然后您可以将它们与 UNION 串在一起以形成一组行,每一行对应更改的每一列。
BEGIN
INSERT INTO ENTITY_AUD (baseTableId, beforeValue, afterValue, changedBy)
SELECT OLD.id, OLD.quantity, NEW.quantity, NEW.updated_by
WHERE OLD.quantity <> NEW.quantity
UNION
SELECT OLD.id, OLD.price, NEW.price, NEW.updated_by
WHERE OLD.price <> NEW.price
UNION
SELECT OLD.id, OLD.billing, NEW.billing, NEW.updated_by
WHERE OLD.billing <> NEW.billing
UNION
...37 others...
END
这个问题过去曾多次出现。这是我找到的一些,但毫无疑问还有更多。
- mysql Trigger for logging, find changed columns (2012)
- MySQL update trigger - find changed columns? (2013)
- Mysql triggers - capture each column change (2014)
- MYSQL update Trigger check changes in all columns and insert values to other table (2019)
- MySQL Trigger compare all columns in OLD and NEW (2021)