これを行う簡単な方法を次に示します。
まず、追跡するデータ テーブルごとに履歴テーブルを作成します (以下のクエリの例)。このテーブルには、データ テーブルの各行に対して実行された各挿入、更新、および削除クエリのエントリがあります。
履歴テーブルの構造は、追跡するデータ テーブルと同じですが、次の 3 つの列が追加されています。発生した操作 (「アクション」と呼びましょう) を格納する列、操作の日時、および列です。シーケンス番号 (「リビジョン」) を格納します。これは操作ごとに増加し、データ テーブルの主キー列によってグループ化されます。
この順序付け動作を行うために、主キー列とリビジョン列に 2 列 (複合) インデックスが作成されます。履歴テーブルで使用されるエンジンが MyISAM である場合にのみ、この方法でシーケンスを実行できることに注意してください (このページの「MyISAM に関する注意事項」を参照してください)。
履歴テーブルの作成は非常に簡単です。以下の ALTER TABLE クエリ (およびその下のトリガー クエリ) で、「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 データベースを実行している間ずっと、この履歴テーブルの実装がオンでしたが、発生した (多くの) パフォーマンスの問題のいずれの原因にもなりませんでした。
挿入が繰り返される場合は、ソフトウェア層で INSERT IGNORE タイプのクエリを確認してください。うーん、今は思い出せませんが、複数の DML アクションを実行した後に最終的に失敗するこのスキームとトランザクションに問題があると思います。少なくとも知っておくべきことがあります。
履歴テーブルとデータ テーブルのフィールドが一致していることが重要です。むしろ、データ テーブルには履歴テーブルよりも多くの列がありません。そうしないと、データ テーブルに対する挿入/更新/削除クエリが失敗し、履歴テーブルへの挿入によって (トリガー クエリの d.* が原因で) 存在しない列がクエリに挿入され、トリガーが失敗します。MySQL にスキーマ トリガーのようなものがあり、列がデータ テーブルに追加された場合に履歴テーブルを変更できるとしたら、すばらしいことです。MySQLは今それを持っていますか? 私は最近Reactをしています:P