审计表的数据更改历史记录:对更改进行分组

sto*_*ofl 7 mysql triggers audit-tables

假设我想将用户和组存储在MySQL数据库中.他们有一个关系n:m.为了跟踪所有更改,每个表都有一个审计表user_journal,group_journal和user_group_journal.MySQL触发器在每次INSERT或UPDATE时将当前记录复制到日志表(不支持DELETES,因为我需要应用程序用户删除记录的信息 - 所以有一个标志active将被设置为0而不是删除).

我的问题是:假设我一次将10个用户添加到一个组中.当我稍后在应用程序的用户界面中单击该组的历史记录时,我希望将这10个用户添加为一步而不是10个独立的步骤.是否有一个很好的解决方案将这些变化组合在一起 也许有可能在每次触发触发器时都有一个递增的计数器?我从未使用触发器.

最好的解决方案是将事务中所做的所有更改放在一起.因此,当用户更新组的名称并在一个步骤中添加10个用户(一个表单控制器调用)时,这将是历史记录中的一个步骤.也许有可能在每次启动事务时定义随机散列或递增全局计数器并在触发器中访问此值?

我不想让表设计比为每个"真实"表都有一个日志表更复杂.我不想在每个数据库表中添加一个事务哈希(意思是"真正的"表,而不是审计表 - 那当然没关系).另外,我想在数据库中有一个解决方案 - 而不是在应用程序中.

sto*_*ofl 4

我玩了一下,现在我找到了一个非常好的解决方案:

\n\n

数据库设置

\n\n
# First of all I create the database and the basic table:\n\nDROP DATABASE `mytest`;\nCREATE DATABASE `mytest`;\nUSE `mytest`;\nCREATE TABLE `test` (\n    `id` INT PRIMARY KEY AUTO_INCREMENT,\n    `something` VARCHAR(255) NOT NULL\n);\n\n# Then I add an audit table to the database:\n\nCREATE TABLE `audit_trail_test` (\n    `_id` INT PRIMARY KEY AUTO_INCREMENT,\n    `_revision_id` VARCHAR(255) NOT NULL,\n    `id` INT NOT NULL,\n    `something` VARCHAR(255) NOT NULL\n);\n\n# I added a field _revision_id to it. This is \n# the ID that groups together all changes a\n# user made within a request of that web\n# application (written in PHP). So we need a\n# third table to store the time and the user\n# that made the changes of that revision:\n\nCREATE TABLE `audit_trail_revisions` (\n    `id` INT PRIMARY KEY AUTO_INCREMENT,\n    `user_id` INT NOT NULL,\n    `time` DATETIME NOT NULL\n);\n\n# Now we need a procedure that creates a\n# record in the revisions table each time an\n# insert or update trigger will be called.\n\nDELIMITER $$\n\nCREATE PROCEDURE create_revision_record()\nBEGIN\n    IF @revision_id IS NULL THEN\n        INSERT INTO `audit_trail_revisions`\n            (user_id, `time`)\n                VALUES\n            (@user_id, @time);\n        SET @revision_id = LAST_INSERT_ID();\n    END IF;\nEND;\n\n# It checks if a user defined variable\n# @revision_id is set and if not it creates\n# the row and stores the generated ID (auto\n# increment) into that variable.\n# \n# Next I wrote the two triggers:\n\nCREATE TRIGGER `test_insert` AFTER INSERT ON `test` \n    FOR EACH ROW BEGIN\n        CALL create_revision_record();\n        INSERT INTO `audit_trail_test`\n            (\n                id,\n                something,\n                _revision_id\n            ) \n        VALUES\n            (\n                NEW.id,\n                NEW.something,\n                @revision_id\n            );\n    END;\n$$\n\nCREATE TRIGGER `test_update` AFTER UPDATE ON `test` \n    FOR EACH ROW BEGIN\n        CALL create_revision_record();\n        INSERT INTO `audit_trail_test`\n            (\n                id,\n                something,\n                _revision_id\n            ) \n        VALUES\n            (\n                NEW.id,\n                NEW.something,\n                @revision_id\n            );\n    END;\n$$\n
Run Code Online (Sandbox Code Playgroud)\n\n

应用程序代码 (PHP)

\n\n
$iUserId = 42;\n\n$Database = new \\mysqli(\'localhost\', \'root\', \'root\', \'mytest\');\n\nif (!$Database->query(\'SET @user_id = \' . $iUserId . \', @time = NOW()\'))\n    die($Database->error);\nif (!$Database->query(\'INSERT INTO `test` VALUES (NULL, "foo")\'))\n    die($Database->error);\nif (!$Database->query(\'UPDATE `test` SET `something` = "bar"\'))\n    die($Database->error);\n\n// To simulate a second request we close the connection,\n// sleep 2 seconds and create a second connection.\n$Database->close();\nsleep(2);\n$Database = new \\mysqli(\'localhost\', \'root\', \'root\', \'mytest\');\n\nif (!$Database->query(\'SET @user_id = \' . $iUserId . \', @time = NOW()\'))\n    die($Database->error);\nif (!$Database->query(\'UPDATE `test` SET `something` = "baz"\'))\n    die($Database->error);\n
Run Code Online (Sandbox Code Playgroud)\n\n

和 \xe2\x80\xa6 结果

\n\n
mysql> select * from test;\n+----+-----------+\n| id | something |\n+----+-----------+\n|  1 | baz       |\n+----+-----------+\n1 row in set (0.00 sec)\n\nmysql> select * from audit_trail_test;\n+-----+--------------+----+-----------+\n| _id | _revision_id | id | something |\n+-----+--------------+----+-----------+\n|   1 | 1            |  1 | foo       |\n|   2 | 1            |  1 | bar       |\n|   3 | 2            |  1 | baz       |\n+-----+--------------+----+-----------+\n3 rows in set (0.00 sec)\n\nmysql> select * from audit_trail_revisions;\n+----+---------+---------------------+\n| id | user_id | time                |\n+----+---------+---------------------+\n|  1 |      42 | 2013-02-03 17:13:20 |\n|  2 |      42 | 2013-02-03 17:13:22 |\n+----+---------+---------------------+\n2 rows in set (0.00 sec)\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我遗漏了一点,请告诉我。我必须action在审计表中添加一列才能记录删除。

\n