sql >> データベース >  >> RDS >> Mysql

レコードへの変更の履歴を追跡するためのMySQLオプション/機能はありますか?

    これを行う簡単な方法は次のとおりです。

    まず、追跡するデータテーブルごとに履歴テーブルを作成します(以下のクエリ例)。このテーブルには、データテーブルの各行で実行される各挿入、更新、および削除クエリのエントリがあります。

    履歴テーブルの構造は、3つの追加の列(発生した操作を格納する列(「アクション」と呼びます)、操作の日時、および列)を除いて、追跡するデータテーブルと同じになります。操作ごとに増分し、データテーブルの主キー列ごとにグループ化されるシーケンス番号(「リビジョン」)を格納します。

    この順序付け動作を行うために、主キー列とリビジョン列に2列(複合)のインデックスが作成されます。履歴テーブルで使用されるエンジンがMyISAM(このページの「MyISAMノート」を参照)

    履歴テーブルはかなり簡単に作成できます。以下のALTERTABLEクエリ(およびその下のトリガークエリ)で、「primary_key_column」をデータテーブル内のその列の実際の名前に置き換えます。

    CREATE TABLE MyDB.data_history LIKE MyDB.data;
    
    ALTER TABLE MyDB.data_history MODIFY COLUMN primary_key_column int(11) NOT NULL, 
       DROP PRIMARY KEY, ENGINE = MyISAM, ADD action VARCHAR(8) DEFAULT 'insert' FIRST, 
       ADD revision INT(6) NOT NULL AUTO_INCREMENT AFTER action,
       ADD dt_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER revision,
       ADD PRIMARY KEY (primary_key_column, revision);
    

    次に、トリガーを作成します:

    DROP TRIGGER IF EXISTS MyDB.data__ai;
    DROP TRIGGER IF EXISTS MyDB.data__au;
    DROP TRIGGER IF EXISTS MyDB.data__bd;
    
    CREATE TRIGGER MyDB.data__ai AFTER INSERT ON MyDB.data FOR EACH ROW
        INSERT INTO MyDB.data_history SELECT 'insert', NULL, NOW(), d.* 
        FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
    
    CREATE TRIGGER MyDB.data__au AFTER UPDATE ON MyDB.data FOR EACH ROW
        INSERT INTO MyDB.data_history SELECT 'update', NULL, NOW(), d.*
        FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
    
    CREATE TRIGGER MyDB.data__bd BEFORE DELETE ON MyDB.data FOR EACH ROW
        INSERT INTO MyDB.data_history SELECT 'delete', NULL, NOW(), d.* 
        FROM MyDB.data AS d WHERE d.primary_key_column = OLD.primary_key_column;
    

    これで完了です。これで、「MyDb.data」のすべての挿入、更新、および削除が「MyDb.data_history」に記録され、このような履歴テーブルが提供されます(考案された「data_columns」列を除く)

    ID    revision   action    data columns..
    1     1         'insert'   ....          initial entry for row where ID = 1
    1     2         'update'   ....          changes made to row where ID = 1
    2     1         'insert'   ....          initial entry, ID = 2
    3     1         'insert'   ....          initial entry, ID = 3 
    1     3         'update'   ....          more changes made to row where ID = 1
    3     2         'update'   ....          changes made to row where ID = 3
    2     2         'delete'   ....          deletion of row where ID = 2 
    

    更新から更新までの特定の列の変更を表示するには、主キー列とシーケンス列で履歴テーブルをそれ自体に結合する必要があります。この目的のために、たとえば次のようなビューを作成できます。

    CREATE VIEW data_history_changes AS 
       SELECT t2.dt_datetime, t2.action, t1.primary_key_column as 'row id', 
       IF(t1.a_column = t2.a_column, t1.a_column, CONCAT(t1.a_column, " to ", t2.a_column)) as a_column
       FROM MyDB.data_history as t1 INNER join MyDB.data_history as t2 on t1.primary_key_column = t2.primary_key_column 
       WHERE (t1.revision = 1 AND t2.revision = 1) OR t2.revision = t1.revision+1
       ORDER BY t1.primary_key_column ASC, t2.revision ASC
    

    編集:うわー、6年前の私の歴史表のような人たち:P

    私の実装はまだ順調に進んでおり、大きくなり、扱いにくくなっていると思います。このデータベースの履歴を確認するためにビューと非常に優れたUIを作成しましたが、これまであまり使用されていなかったと思います。

    いくつかのコメントに順不同で対処するには:

    • 私はもう少し複雑なPHPで独自の実装を行い、コメントで説明されている問題のいくつかを回避しました(インデックスが大幅に転送されます。一意のインデックスを履歴テーブルに転送すると、問題が発生します。解決策があります。コメントでこれ)。この手紙への投稿に従うことは、データベースがどの程度確立されているかによっては、冒険になる可能性があります。

    • 主キーとリビジョン列の関係がずれているように見える場合は、通常、複合キーが何らかの理由で中断されていることを意味します。まれに、これが発生し、原因がわからなくなったことがありました。

    • このソリューションは、トリガーを使用するのと同じように、かなりパフォーマンスが高いことがわかりました。また、MyISAMは挿入が高速であり、これがすべてのトリガーで実行されます。スマートインデックス(または...の欠如)を使用して、これをさらに改善できます。主キーを使用してMyISAMテーブルに単一の行を挿入することは、他の場所で重大な問題が発生している場合を除いて、実際には最適化する必要のある操作ではありません。この履歴テーブルの実装がオンになっているMySQLデータベースを実行している間、発生した(多くの)パフォーマンスの問題の原因にはなりませんでした。

    • 挿入が繰り返される場合は、ソフトウェアレイヤーでINSERTIGNOREタイプのクエリを確認してください。うーん、今は思い出せませんが、このスキームとトランザクションには問題があり、複数のDMLアクションを実行すると最終的に失敗すると思います。少なくとも注意すべき点。

    • 履歴テーブルとデータテーブルのフィールドが一致していることが重要です。または、むしろ、データテーブルに履歴テーブルよりも多くの列がないこと。そうしないと、履歴テーブルへの挿入によってクエリに存在しない列が配置された場合(トリガークエリのd。*が原因)、データテーブルへの挿入/更新/削除クエリが失敗し、トリガーが失敗します。 MySQLにスキーマトリガーのようなものがあり、列がデータテーブルに追加された場合に履歴テーブルを変更できるとしたら、すばらしいでしょう。 MySQLには今それがありますか?私は最近Reactをします:P



    1. 関連するtnsnames.oraファイルの場所を特定する

    2. データベースへのユーザーアクセスを制限する必要がある理由

    3. 複数のテーブルを検索し、結果の行にテーブル名を表示します

    4. OracleSQLピボットクエリ