7

ユーザーとグループを MySQL データベースに保存したいとします。それらは n:m の関係にあります。すべての変更を追跡するために、各テーブルには監査テーブル user_journal、group_journal、および user_group_journal があります。MySQLトリガーは、INSERTまたはUPDATEごとに現在のレコードをジャーナルテーブルにコピーします(アプリケーションユーザーがレコードを削除したという情報が必要になるため、DELETESはサポートされていません-削除の代わりにactive設定されるフラグがあります0)。

私の質問/問題は次のとおりです。一度に 10 人のユーザーをグループに追加すると仮定します。後でアプリケーションのユーザー インターフェイスでそのグループの履歴をクリックするときに、これらの 10 人のユーザーの追加を10 の独立したステップとしてではなく、1 つのステップとして表示したいと考えています。そのような変更をグループ化するための良い解決策はありますか? トリガーがトリガーされるたびにインクリメントされるカウンターを持つことは可能でしょうか?トリガーを使用したことはありません。

最善の解決策は、トランザクション内で行われたすべての変更をまとめることです。したがって、ユーザーがグループの名前を更新し、1 つのステップ (1 つのフォーム コントローラー呼び出し) で 10 人のユーザーを追加すると、これは履歴の 1 つのステップになります。トランザクションが開始されるたびにランダム ハッシュを定義したり、グローバル カウンターをインクリメントしたりして、トリガーでこの値にアクセスすることは可能でしょうか?

「実際の」テーブルごとに 1 つのジャーナル テーブルを作成するよりも、テーブルの設計を複雑にしたくありません。各データベース テーブルにトランザクション ハッシュを追加したくありません (監査テーブルではなく、「実際の」テーブルを意味します。もちろん問題ありません)。また、アプリケーションではなく、データベースでソリューションを提供したいと考えています。

4

2 に答える 2

3

私は少し遊んで、今では非常に良い解決策を見つけました:

データベースのセットアップ

# First of all I create the database and the basic table:

DROP DATABASE `mytest`;
CREATE DATABASE `mytest`;
USE `mytest`;
CREATE TABLE `test` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `something` VARCHAR(255) NOT NULL
);

# Then I add an audit table to the database:

CREATE TABLE `audit_trail_test` (
    `_id` INT PRIMARY KEY AUTO_INCREMENT,
    `_revision_id` VARCHAR(255) NOT NULL,
    `id` INT NOT NULL,
    `something` VARCHAR(255) NOT NULL
);

# I added a field _revision_id to it. This is 
# the ID that groups together all changes a
# user made within a request of that web
# application (written in PHP). So we need a
# third table to store the time and the user
# that made the changes of that revision:

CREATE TABLE `audit_trail_revisions` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `user_id` INT NOT NULL,
    `time` DATETIME NOT NULL
);

# Now we need a procedure that creates a
# record in the revisions table each time an
# insert or update trigger will be called.

DELIMITER $$

CREATE PROCEDURE create_revision_record()
BEGIN
    IF @revision_id IS NULL THEN
        INSERT INTO `audit_trail_revisions`
            (user_id, `time`)
                VALUES
            (@user_id, @time);
        SET @revision_id = LAST_INSERT_ID();
    END IF;
END;

# It checks if a user defined variable
# @revision_id is set and if not it creates
# the row and stores the generated ID (auto
# increment) into that variable.
# 
# Next I wrote the two triggers:

CREATE TRIGGER `test_insert` AFTER INSERT ON `test` 
    FOR EACH ROW BEGIN
        CALL create_revision_record();
        INSERT INTO `audit_trail_test`
            (
                id,
                something,
                _revision_id
            ) 
        VALUES
            (
                NEW.id,
                NEW.something,
                @revision_id
            );
    END;
$$

CREATE TRIGGER `test_update` AFTER UPDATE ON `test` 
    FOR EACH ROW BEGIN
        CALL create_revision_record();
        INSERT INTO `audit_trail_test`
            (
                id,
                something,
                _revision_id
            ) 
        VALUES
            (
                NEW.id,
                NEW.something,
                @revision_id
            );
    END;
$$

アプリケーション コード (PHP)

$iUserId = 42;

$Database = new \mysqli('localhost', 'root', 'root', 'mytest');

if (!$Database->query('SET @user_id = ' . $iUserId . ', @time = NOW()'))
    die($Database->error);
if (!$Database->query('INSERT INTO `test` VALUES (NULL, "foo")'))
    die($Database->error);
if (!$Database->query('UPDATE `test` SET `something` = "bar"'))
    die($Database->error);

// To simulate a second request we close the connection,
// sleep 2 seconds and create a second connection.
$Database->close();
sleep(2);
$Database = new \mysqli('localhost', 'root', 'root', 'mytest');

if (!$Database->query('SET @user_id = ' . $iUserId . ', @time = NOW()'))
    die($Database->error);
if (!$Database->query('UPDATE `test` SET `something` = "baz"'))
    die($Database->error);

そして…結果

mysql> select * from test;
+----+-----------+
| id | something |
+----+-----------+
|  1 | baz       |
+----+-----------+
1 row in set (0.00 sec)

mysql> select * from audit_trail_test;
+-----+--------------+----+-----------+
| _id | _revision_id | id | something |
+-----+--------------+----+-----------+
|   1 | 1            |  1 | foo       |
|   2 | 1            |  1 | bar       |
|   3 | 2            |  1 | baz       |
+-----+--------------+----+-----------+
3 rows in set (0.00 sec)

mysql> select * from audit_trail_revisions;
+----+---------+---------------------+
| id | user_id | time                |
+----+---------+---------------------+
|  1 |      42 | 2013-02-03 17:13:20 |
|  2 |      42 | 2013-02-03 17:13:22 |
+----+---------+---------------------+
2 rows in set (0.00 sec)

見落としている点があれば教えてください。action削除を記録できるように、監査テーブルに列を追加する必要があります。

于 2013-02-03T16:18:21.550 に答える
2

ユーザーのバッチをグループに追加する割合が1秒に1回未満であると仮定します。

とのような名前のタイムスタンプ型の列を追加することをお勧めadded_timestampuser_groupますuser_group_journalこれを自動更新タイムスタンプにしたり、デフォルトでCURRENT_TIMESTAMPに設定したりしないでください。代わりに、コードでuser_groupにバッチで挿入するときに、現在の日付と時刻を計算してから、すべての新しいuser_groupレコードに対してこれを手動で設定してください。

user_group新しいレコードの残りの部分をテーブルにコピーするフィールドを追加するには、セットアップを微調整する必要がある場合がありuser_group_journalます。

group_id次に、と新しいadded_timestamp列をグループ化するクエリ/ビューを作成できる場合。

より忠実なものが必要な場合は、1秒後に文字列列を使用して、より詳細な時間の文字列表現を入力できます(使用する言語で許可されているライブラリを生成する必要があります)。

于 2013-01-01T17:20:38.500 に答える