Вот простой способ сделать это:
Сначала создайте таблицу истории для каждой таблицы данных, которую вы хотите отслеживать (пример запроса ниже). В этой таблице будет запись для каждого запроса вставки, обновления и удаления, выполняемого для каждой строки в таблице данных.
Структура таблицы истории будет такой же, как и таблица данных, которую она отслеживает, за исключением трех дополнительных столбцов: столбца для хранения произошедшей операции (назовем его «действие»), даты и времени операции и столбца. для хранения порядкового номера («редакция»), который увеличивается за каждую операцию и группируется по столбцу первичного ключа таблицы данных.
Для выполнения этого поведения последовательности создается двухколоночный (составной) индекс для столбца первичного ключа и столбца ревизии. Обратите внимание, что вы можете выполнять последовательность таким образом, только если движком, используемым таблицей истории, является 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
Моя реализация все еще продолжается, я полагаю, она становится все больше и громоздче. Я написал представления и довольно приятный пользовательский интерфейс для просмотра истории в этой базе данных, но я не думаю, что он когда-либо широко использовался. Такие дела.
Чтобы ответить на некоторые комментарии в произвольном порядке:
Я сделал свою собственную реализацию на PHP, которая была немного более сложной, и избежала некоторых проблем, описанных в комментариях (значительная передача индексов. Если вы перенесете уникальные индексы в таблицу истории, все сломается. Есть решения для это в комментариях). Следование этому посту в письме может быть приключением, в зависимости от того, насколько создана ваша база данных.
Если связь между первичным ключом и столбцом ревизии кажется нарушенной, это обычно означает, что составной ключ каким-то образом заблокирован. В нескольких редких случаях это случалось со мной, и я не мог понять причины.
Я обнаружил, что это решение довольно производительное, поскольку оно использует триггеры. Кроме того, MyISAM быстро выполняет вставку, что и делают все триггеры. Вы можете улучшить это дополнительно с помощью интеллектуальной индексации (или отсутствия ...). Вставка одной строки в таблицу MyISAM с первичным ключом не должна быть операцией, которую вам нужно оптимизировать, на самом деле, если у вас нет серьезных проблем, происходящих в другом месте. За все время, пока я работал с базой данных MySQL, эта реализация таблицы истории использовалась, она никогда не была причиной каких-либо (многих) возникающих проблем с производительностью.
если вы получаете повторяющиеся вставки, проверьте свой программный уровень на предмет запросов типа INSERT IGNORE. Хммм, сейчас не могу вспомнить, но я думаю, что есть проблемы с этой схемой и транзакциями, которые в конечном итоге терпят неудачу после выполнения нескольких действий DML. По крайней мере, кое-что, о чем нужно знать.
Важно, чтобы поля в таблице истории и таблице данных совпадали. Или, скорее, ваша таблица данных не имеет БОЛЬШЕ столбцов, чем таблица истории. В противном случае запросы вставки / обновления / удаления в таблице данных завершатся ошибкой, когда вставки в таблицы истории помещают столбцы в запрос, которые не существуют (из-за d. * В запросах триггера), и триггер не работает. Было бы здорово, если бы в MySQL было что-то вроде схем-триггеров, в которых вы могли бы изменить таблицу истории, если бы столбцы были добавлены в таблицу данных. Есть ли это в MySQL сейчас? Сейчас я React: P