用于自动创建审计(历史)表和触发器的 MySQL 脚本?

Joh*_*tle 5 mysql audit scripting

对于监管和欺诈要求,我们需要记录对 DB 中大多数表的每次更改。我们在以前的项目中非常成功地做到这一点的方法是拥有每个表的副本,除了:

  1. 4 个附加列:DateChanged、UserId、Action(创建、更新、删除)和 IP
  2. Id pk 不再是唯一的。
  3. 删除了所有其他约束(FK、唯一索引等)。
  4. 审计表位于单独的审计模式中

在以前的工作中,Oracle DBA 编写了一个脚本来自动生成所有这些,它执行以下操作:

  1. 如果不存在,则创建一个名为审计的新架构
  2. 迭代正常模式中的每个表 t:
    1. 在审计模式中创建了一个具有相同表名的新表,但在它前面加上了 a_ 前缀,例如 a_t
    2. 添加与原始表相同的所有列,以及 4 个附加列(DateChanged、UserId、IP Address 和 Action)
    3. 生成触发器并将其添加到原始表(如果尚未存在)以:
    4. 如果更新,则将新行写入相应的 a_ 表,“更新”操作包含所有列中的预更新(旧)值(主表将具有新值)
    5. 如果删除,添加一行是主表行的副本,但操作 =“删除”。
    6. 如果插入,则将该行添加到审计模式

笔记:

  1. 所有表都有一个 ID PK。
  2. 出于性能考虑或因为不需要而需要排除一些表(例如,由触发器更新的余额表)

该系统的美妙之处在于您可以查询更改的内容和时间,或者谁对哪些内容进行了更改,并查看更改前后的记录。更改发生在 DB 行级别,而不是单个列级别。

有没有人在 MYSQL 5.6 中遇到过这样的事情?我们团队中没有 DBA 可以从头开始编写这样的东西,但我们知道足以修改类似的东西。

van*_*nyo 5

我今天早些时候刚刚写了这个。它是一个处理 information_schema 数据库的选择语句,它为审计表和触发器生成模式。

SET GLOBAL group_concat_max_len = 1000;

SET @dbName = "[[[your_db_name_here]]]";

SELECT concat("DROP TABLE IF EXISTS `", @dbName, "`.`", table_data.audit_table, "`;\r",
          "CREATE TABLE `", @dbName, "`.`", table_data.audit_table, "`\r",
          "(\r",
          "  `auditAction` ENUM ('INSERT', 'UPDATE', 'DELETE'),\r",
          "  `auditTimestamp` DATETIME DEFAULT CURRENT_TIMESTAMP,\r",
          "  `auditId` INT(14) AUTO_INCREMENT,",
          column_defs, ",\r"
          "  PRIMARY KEY (`auditId`),\r",
          "  INDEX (`auditTimestamp`)\r",
          ")\r",
          "  ENGINE = InnoDB;\r\r",
          "DROP TRIGGER IF EXISTS `", @dbName, "`.`", table_data.insert_trigger, "`;\r",
          "CREATE TRIGGER `", @dbName, "`.`", table_data.insert_trigger, "`\r",
          "  AFTER INSERT ON `", @dbName, "`.`", table_data.db_table, "`\r",
          "  FOR EACH ROW INSERT INTO `", @dbName, "`.`", table_data.audit_table, "`\r",
          "     (`auditAction`,", table_data.column_names, ")\r",
          "  VALUES\r",
          "     ('INSERT',", table_data.NEWcolumn_names, ");\r\r",
          "DROP TRIGGER IF EXISTS `", @dbName, "`.`", table_data.update_trigger, "`;\r",
          "CREATE TRIGGER `", @dbName, "`.`", table_data.update_trigger, "`\r",
          "  AFTER UPDATE ON `", @dbName, "`.`", table_data.db_table, "`\r",
          "  FOR EACH ROW INSERT INTO `", @dbName, "`.`", table_data.audit_table, "`\r",
          "     (`auditAction`,", table_data.column_names, ")\r",
          "  VALUES\r",
          "     ('UPDATE',", table_data.NEWcolumn_names, ");\r\r",
          "DROP TRIGGER IF EXISTS `", @dbName, "`.`", table_data.delete_trigger, "`;\r",
          "CREATE TRIGGER `", @dbName, "`.`", table_data.delete_trigger, "`\r",
          "  AFTER DELETE ON `", @dbName, "`.`", table_data.db_table, "`\r",
          "  FOR EACH ROW INSERT INTO `", @dbName, "`.`", table_data.audit_table, "`\r",
          "     (`auditAction`,", table_data.column_names, ")\r",
          "  VALUES\r",
          "     ('DELETE',", table_data.OLDcolumn_names, ");\r\r"
)
FROM (
   # This select builds a derived table of table names with ordered and grouped column information in different
   # formats as needed for audit table definitions and trigger definitions.
   SELECT
     table_order_key,
     table_name                                                                      AS db_table,
     concat("audit_", table_name)                                                    AS audit_table,
     concat(table_name, "_inserts")                                                  AS insert_trigger,
     concat(table_name, "_updates")                                                  AS update_trigger,
     concat(table_name, "_deletes")                                                  AS delete_trigger,
     group_concat("\r  `", column_name, "` ", column_type ORDER BY column_order_key) AS column_defs,
     group_concat("`", column_name, "`" ORDER BY column_order_key)                   AS column_names,
     group_concat("`NEW.", column_name, "`" ORDER BY column_order_key)               AS NEWcolumn_names,
     group_concat("`OLD.", column_name, "`" ORDER BY column_order_key)               AS OLDcolumn_names
   FROM
     (
       # This select builds a derived table of table names, column names and column types for
       # non-audit tables of the specified db, along with ordering keys for later order by.
       # The ordering must be done outside this select, as tables (including derived tables)
       # are by definition unordered.
       # We're only ordering so that the generated audit schema maintains a resemblance to the
       # main schema.
       SELECT
         information_schema.tables.table_name        AS table_name,
         information_schema.columns.column_name      AS column_name,
         information_schema.columns.column_type      AS column_type,
         information_schema.tables.create_time       AS table_order_key,
         information_schema.columns.ordinal_position AS column_order_key
       FROM information_schema.tables
         JOIN information_schema.columns
           ON information_schema.tables.table_name = information_schema.columns.table_name
       WHERE information_schema.tables.table_schema = @dbName
             AND information_schema.columns.table_schema = @dbName
             AND information_schema.tables.table_name NOT LIKE "audit\_%"
     ) table_column_ordering_info
   GROUP BY table_name
 ) table_data
ORDER BY table_order_key
INTO OUTFILE "[[[your_output_file]]]"
Run Code Online (Sandbox Code Playgroud)