Ось простий спосіб зробити це:
Спочатку створіть таблицю історії для кожної таблиці даних, яку ви хочете відстежувати (приклад запиту нижче). Ця таблиця містить запис для кожного запиту на вставку, оновлення та видалення, виконаний у кожному рядку таблиці даних.
Структура таблиці історії буде такою ж, як таблиця даних, яку вона відстежує, за винятком трьох додаткових стовпців: стовпчик для зберігання операції, що відбулася (назвемо це "дія"), дата та час операції та стовпець щоб зберегти порядковий номер ('редакція'), який збільшується на одну операцію і групується за стовпцем первинного ключа таблиці даних.
Для цього поведінки послідовності створюється два колонки (складений) індекс у стовпці первинного ключа та стовпці ревізії. Зауважте, що ви можете робити послідовності таким чином, лише якщо двигун, який використовується таблицею історії, є MyISAM ( див. "Примітки MyISAM" на цій сторінці)
Таблицю історії створити досить просто. У запиті ALTER TABLE нижче (і в тригерних запитах нижче цього) замініть "basic_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_column")
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
Я вважаю, що моє реалізація все ще гуде, стає все більшим і більш непридатним. Я написав погляди і досить приємний інтерфейс, щоб подивитися на історію в цій базі даних, але я не думаю, що вона ніколи не використовувалася багато. Тому вона йде.
Щоб звертатися до деяких коментарів у конкретному порядку:
Я зробив власну реалізацію в PHP, яка була трохи більше задіяна, і уникнула деяких проблем, описаних у коментарях (перенесення індексів помітно. Якщо ви перенесете унікальні індекси в таблицю історії, все порушиться. Є рішення для це в коментарях). Слідування за цим повідомленням до листа може стати пригодою, залежно від того, наскільки створена ваша база даних.
Якщо взаємозв'язок між первинним ключем і стовпцем ревізії здається вимкненим, це зазвичай означає, що складений ключ якось замикається. У кількох рідкісних випадках у мене траплялося таке, і я втрачав причину.
Я знайшов це рішення досить ефективним, використовуючи тригери, як це робиться. Крім того, MyISAM швидкий при вставці, що і всі тригери. Ви можете це покращити за допомогою інтелектуального індексування (або відсутності ...). Вставлення одного рядка в таблицю MyISAM з первинним ключем не повинно бути операцією, яку потрібно оптимізувати, якщо тільки у вас не виникають суттєві проблеми в іншому місці. Протягом усього часу, коли я працював у базі даних MySQL, на якій реалізовувалася ця таблиця історії, це ніколи не було причиною виникнення будь-яких (багатьох) проблем з продуктивністю.
якщо ви отримуєте повторні вставки, перевірте свій програмний рівень на наявність запитів типу INSERT IGNORE. Hrmm, не можу зараз згадати, але я думаю, що є проблеми з цією схемою та транзакції, які в кінцевому рахунку виходять з ладу після виконання декількох дій DML. Щось слід пам’ятати, принаймні.
Важливо, щоб поля в таблиці історії та таблиці даних співпадали. Або, вірніше, у вашій таблиці даних немає БОЛЬШІ стовпців, ніж у таблиці історії. В іншому випадку запити вставити / оновити / del у таблицю даних не вдасться, коли вставки до таблиць історії помістять стовпці в запит, який не існує (через d * в запитах тригера), і тригер не працює. t було б дивним, якби MySQL мав щось подібне до тригерів схеми, де ви могли б змінити таблицю історії, якби стовпці були додані до таблиці даних. Чи є у MySQL це зараз? Я реагую в ці дні: P