これを行う簡単な方法を次に示します。
最初に、追跡するデータテーブルごとに履歴テーブルを作成します(以下のクエリ例)。このテーブルには、データテーブルの各行で実行される挿入、更新、削除の各クエリのエントリがあります。
履歴テーブルの構造は、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にはそれがありますか?私は最近反応します:P