【问题标题】:Create generic trigger in MySQL在 MySQL 中创建通用触发器
【发布时间】:2019-02-12 15:15:52
【问题描述】:

在 Postgresql 中,可以使用trigger procedure 创建触发器。这是创建触发器的便捷方式。使用相同的触发器过程,可以创建多个触发器并将其应用于多个不同的表。我想知道是否有任何 MySQL 等价物。我受到这个博客 post 的启发,它为数据库审计创建了一个通用触发器。我的计划是使用 MySQL 来实现类似的方法。但是,真的可以通过 MySQL 创建那种通用触发器吗?

【问题讨论】:

  • 审计有很多方面比触发器更实用。大致了解您的要求的实施情况,并具体说明您想要审核表格的内容。

标签: mysql postgresql triggers


【解决方案1】:

在做了一些研究之后,我了解到在 MySQL 中没有直接的方法来创建通用触发器。即使是动态 SQL,如 prepare 语句,execute 语句也不允许在 MySQL 中的 trigger 中使用。

我找到了一种动态生成触发器的解决方法。假设我们有一个客户表:

CREATE TABLE customer (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  created_on datetime DEFAULT NULL,
  first_name varchar(100) NOT NULL,
  last_name varchar(100) NOT NULL,
  PRIMARY KEY (id)
) 

修订信息表:

CREATE TABLE REVINFO (
  REV int(11) NOT NULL AUTO_INCREMENT,
  REVTSTMP bigint(20) DEFAULT NULL,
  PRIMARY KEY (REV)
) 

审计表:

CREATE TABLE customer_AUD (
  id bigint(20) NOT NULL,
  REV int(11) NOT NULL,
  REVTYPE tinyint(4) DEFAULT NULL,
  created_on datetime DEFAULT NULL,
  first_name varchar(100) DEFAULT NULL,
  last_name varchar(100) DEFAULT NULL,
  PRIMARY KEY (id, REV),
  KEY FK_REV (REV)
) 

现在我们将创建一个过程,该过程将采用表名并生成 SQL 以为表创建与审计相关的触发器。

DROP PROCEDURE IF EXISTS `proc_trigger_generator`;

DELIMITER $$

CREATE PROCEDURE `proc_trigger_generator` (IN tableName VARCHAR(255))
BEGIN
    DECLARE triggerSQL TEXT DEFAULT "";
    DECLARE cols TEXT DEFAULT "";
    DECLARE col_values TEXT DEFAULT "";
    DECLARE insert_query TEXT DEFAULT "";
    DECLARE colName TEXT DEFAULT "";
    DECLARE done INT DEFAULT FALSE;

    DECLARE cursorDS CURSOR FOR SELECT column_name FROM information_schema.columns cols 
        WHERE cols.table_name = CONCAT(tableName, '_AUD')
            and (column_name != 'REV' && column_name != 'REVTYPE');
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 

    SET triggerSQL = 'DELIMITER ;; \n\n';
    SET triggerSQL = CONCAT(triggerSQL, 'drop trigger if exists tr_', tableName, '_update_audit;; \n\n');
    SET triggerSQL = CONCAT(triggerSQL, 'create trigger tr_', tableName, '_update_audit \n');
    SET triggerSQL = CONCAT(triggerSQL, 'after update \n');
    SET triggerSQL = CONCAT(triggerSQL, '\t on ', tableName, '\n');
    SET triggerSQL = CONCAT(triggerSQL, 'for each row \n');
    SET triggerSQL = CONCAT(triggerSQL, 'begin  \n');
    SET triggerSQL = CONCAT(triggerSQL, '\t DECLARE tmpInt INT; \n');   
    SET triggerSQL = CONCAT(triggerSQL, '\t SELECT COALESCE(MAX(REV), 0) FROM REVINFO into tmpInt; \n\n');  
    SET triggerSQL = CONCAT(triggerSQL, '\t INSERT INTO REVINFO (REV, REVTSTMP) VALUES (tmpInt+1, CURRENT_TIMESTAMP()); \n\n'); 

    SET insert_query = CONCAT(insert_query, 'INSERT INTO ', CONCAT(tableName, '_AUD'), ' (');
    OPEN cursorDS;  
    ds_loop: LOOP
        FETCH cursorDS INTO colName;
        IF done THEN
            LEAVE ds_loop;
        END IF;     
        SET cols = CONCAT(cols, colName, ', ');
        SET col_values = CONCAT(col_values, 'new.', colName, ', ');
    END LOOP;

    SET insert_query = CONCAT(insert_query, cols, 'REV, REVTYPE) VALUES \n');
    SET insert_query = CONCAT(insert_query, '\t\t(', col_values, 'tmpInt+1, 1', ');');
    CLOSE cursorDS;

    SET triggerSQL = CONCAT(triggerSQL, '\t ',insert_query, ' \n\n');
    SET triggerSQL = CONCAT(triggerSQL, 'end;; \n\n');
    SET triggerSQL = CONCAT(triggerSQL, 'DELIMITER ; \n\n');

    SELECT triggerSQL;

END $$
DELIMITER ;

call proc_trigger_generator('customer');

使用客户表名调用过程为所需触发器生成 SQL:

DELIMITER ;; 

drop trigger if exists tr_customer_update_audit;; 

create trigger tr_customer_update_audit 
after update 
        on customer
for each row 
begin  
     DECLARE tmpInt INT; 
     SELECT COALESCE(MAX(REV), 0) FROM REVINFO into tmpInt; 

     INSERT INTO REVINFO (REV, REVTSTMP) VALUES (tmpInt+1, CURRENT_TIMESTAMP()); 

     INSERT INTO customer_AUD (id, created_on, first_name, last_name, REV, REVTYPE) VALUES 
        (new.id, new.created_on, new.first_name, new.last_name, tmpInt+1, 1); 

end;; 

DELIMITER ; 

上述触发器应该对客户表执行审计任务。 现在我们希望应用审计相关任务,触发器生成器过程可以应用于任何其他表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 2011-02-03
    • 2016-09-19
    • 1970-01-01
    • 2015-03-01
    • 2013-12-12
    • 1970-01-01
    相关资源
    最近更新 更多