有没有一种解决方法可以使用预准备语句在 MySQL 中创建触发器

问题描述 投票:0回答:1
我正在尝试在 MySQL 数据库中自动创建审核表。我创建了一个脚本,它将自动为给定架构中没有相应列和数据类型的每个表创建一个审计表。例如,我的数据库架构有一个名为 my_table 的表,该脚本将创建另一个名为audit_my_table 的表。现在我想向 my_table 添加触发器,以便每当插入或修改数据时都会在审核表中创建一条记录。我最近了解到 MySQL 中的准备语句不允许使用 CREATE TRIGGER 命令。我对 MySQL 和 SQL 的了解总体上还很有限,所以我目前不知道是否有合适的解决方法可以得到相同的结果。对于上下文,我将包含创建审核表的代码。

DROP PROCEDURE IF EXISTS CreateAuditTriggers; DELIMITER // CREATE PROCEDURE CreateAuditTriggers(IN schemaName VARCHAR(64)) BEGIN -- Declare variables DECLARE done INT DEFAULT FALSE; DECLARE tblName VARCHAR(64); DECLARE auditTblName VARCHAR(64); DECLARE colName VARCHAR(64); DECLARE colType VARCHAR(64); DECLARE triggerSQL LONGTEXT; -- Declare cursors DECLARE tblCursor CURSOR FOR SELECT t.table_name FROM INFORMATION_SCHEMA.TABLES t WHERE t.TABLE_SCHEMA = schemaName AND t.TABLE_NAME NOT LIKE 'audit_%'; DECLARE colCursor CURSOR FOR SELECT column_name, column_type FROM information_schema.columns WHERE table_schema = schemaName AND table_name = tblName; -- Declare handlers DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- Open table cursor OPEN tblCursor; -- Loop through each table tbl_loop: LOOP FETCH tblCursor INTO tblName; IF done THEN LEAVE tbl_loop; END IF; -- Set the audit table name SET auditTblName = CONCAT('audit_', tblName); -- Check if the audit table exists IF (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = schemaName AND TABLE_NAME = auditTblName) = 0 THEN -- Audit table does not exist, skip this table ITERATE tbl_loop; END IF; -- Start generating the trigger SQL SET triggerSQL = CONCAT(' CREATE TRIGGER ', tblName, '_before_insert BEFORE INSERT ON ', tblName, ' FOR EACH ROW BEGIN INSERT INTO ', auditTblName, ' ('); -- Open column cursor OPEN colCursor; -- Loop through columns to generate SQL col_loop: LOOP FETCH colCursor INTO colName, colType; IF done THEN LEAVE col_loop; END IF; -- Add column to trigger SQL SET triggerSQL = CONCAT(triggerSQL, colName, ', '); END LOOP; -- Close column cursor CLOSE colCursor; -- Finalize trigger SQL SET triggerSQL = TRIM(TRAILING ', ' FROM triggerSQL); SET triggerSQL = CONCAT(triggerSQL, ') VALUES ('); -- Re-open column cursor to append values OPEN colCursor; col_loop: LOOP FETCH colCursor INTO colName, colType; IF done THEN LEAVE col_loop; END IF; -- Add column values to trigger SQL SET triggerSQL = CONCAT(triggerSQL, 'NEW.', colName, ', '); END LOOP; -- Close column cursor CLOSE colCursor; -- Finalize the trigger SQL SET triggerSQL = TRIM(TRAILING ', ' FROM triggerSQL); SET triggerSQL = CONCAT(triggerSQL, '); END;'); -- Execute the trigger creation SQL SET @sql = triggerSQL; PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- Reset done flag for the next iteration SET done = FALSE; END LOOP; -- Close table cursor CLOSE tblCursor; END // DELIMITER ;
    
sql mysql triggers prepared-statement
1个回答
0
投票
我可以想到两种解决方法:

解决方法1.

使用客户端语言,而不是存储过程语言。这允许您一次性将 CREATE TRIGGER 语句格式化为客户端语言中的字符串,并执行它。如果您这样做,则无需准备和执行。而在存储过程语言中,没有立即执行函数;要编写动态语句,您所能做的就是使用准备和执行。

事实上,我经常建议避免在任何任务中使用 MySQL 存储过程语言。

解决方法2.

不要使用触发器,而是使用

MySQL 审核日志插件。这可能比使用触发器更通用、更安全、更高效。

MySQL 提供的审计插件仅供企业客户使用,但也有免费的开源实现,例如

Percona Audit Log Plugin

© www.soinside.com 2019 - 2024. All rights reserved.