Это тонко.
Если бизнес-требование звучит так: «Я хочу проверить изменения данных - кто что сделал и когда?», Вы обычно можете использовать таблицы аудита (согласно примеру триггера, опубликованному Китанджаном). Я не большой поклонник триггеров, но у них есть большое преимущество в том, что они относительно безболезненны в реализации - вашему существующему коду не нужно знать о триггерах и материалах аудита.
Если бизнес-требование гласит: «Покажите мне, какое состояние данных было на заданную дату в прошлом», это означает, что в ваше решение вошел аспект изменения с течением времени. Хотя вы можете практически восстановить состояние базы данных, просто взглянув на таблицы аудита, это сложно и подвержено ошибкам, а для любой сложной логики базы данных это становится громоздким. Например, если бизнес хочет знать, «найти адреса писем, которые мы должны были отправить клиентам, у которых были неоплаченные неоплаченные счета в первый день месяца», вам, вероятно, придется просмотреть полдюжины таблиц аудита.
Вместо этого вы можете встроить концепцию изменений с течением времени в свою схему (это второй вариант, который предлагает Китанджан). Это изменение вашего приложения, определенно на уровне бизнес-логики и персистентности, поэтому это нетривиально.
Например, если у вас есть такая таблица:
CUSTOMER
---------
CUSTOMER_ID PK
CUSTOMER_NAME
CUSTOMER_ADDRESS
и вы хотите отслеживать с течением времени, вы бы изменили его следующим образом:
CUSTOMER
------------
CUSTOMER_ID PK
CUSTOMER_VALID_FROM PK
CUSTOMER_VALID_UNTIL PK
CUSTOMER_STATUS
CUSTOMER_USER
CUSTOMER_NAME
CUSTOMER_ADDRESS
Каждый раз, когда вы хотите изменить запись о клиенте, вместо обновления записи вы устанавливаете VALID_UNTIL в текущей записи на NOW () и вставляете новую запись с VALID_FROM (сейчас) и нулевым значением VALID_UNTIL. Вы устанавливаете статус «CUSTOMER_USER» на идентификатор входа текущего пользователя (если вам нужно его сохранить). Если необходимо удалить клиента, вы можете указать это с помощью флага CUSTOMER_STATUS - вы никогда не сможете удалить записи из этой таблицы.
Таким образом, вы всегда можете узнать, каков был статус таблицы клиентов на заданную дату - каков был адрес? Они изменили название? Присоединяясь к другим таблицам с аналогичными датами valid_from и valid_until, вы можете восстановить всю картину исторически. Чтобы узнать текущий статус, вы ищите записи с нулевой датой VALID_UNTIL.
Это громоздко (строго говоря, valid_from вам не нужен, но это немного упрощает запросы). Это усложняет ваш дизайн и доступ к базе данных. Но это значительно упрощает реконструкцию мира.
Вот простой способ сделать это:
Сначала создайте таблицу истории для каждой таблицы данных, которую вы хотите отслеживать (пример запроса ниже). В этой таблице будет запись для каждого запроса вставки, обновления и удаления, выполняемого для каждой строки в таблице данных.
Структура таблицы истории будет такой же, как и таблица данных, которую она отслеживает, за исключением трех дополнительных столбцов: столбца для хранения произошедшей операции (назовем его «действие»), даты и времени операции и столбца. для хранения порядкового номера («редакция»), который увеличивается за каждую операцию и группируется по столбцу первичного ключа таблицы данных.
Для выполнения этого поведения последовательности создается двухколоночный (составной) индекс для столбца первичного ключа и столбца ревизии. Обратите внимание, что вы можете выполнять последовательность таким образом, только если движком, используемым таблицей истории, является MyISAM ( см. «Примечания MyISAM» на этой странице)
Таблицу истории создать довольно просто. В запросе ALTER TABLE ниже (и в запросах триггеров ниже) замените primary_key_column фактическим именем этого столбца в таблице данных.
Затем вы создаете триггеры:
И вы сделали. Теперь все вставки, обновления и удаления в MyDb.data будут записаны в MyDb.data_history, давая вам такую таблицу истории (за вычетом надуманного столбца data_columns).
Чтобы отобразить изменения для данного столбца или столбцов от обновления к обновлению, вам необходимо присоединить таблицу истории к самой себе по столбцам первичного ключа и последовательности. Вы можете создать представление для этой цели, например:
Изменить: Ого, людям нравится моя таблица истории 6 лет назад: P
Моя реализация все еще продолжается, я полагаю, она становится все больше и громоздче. Я написал представления и довольно приятный пользовательский интерфейс для просмотра истории в этой базе данных, но я не думаю, что он когда-либо широко использовался. Такие дела.
Чтобы ответить на некоторые комментарии в произвольном порядке:
Я сделал свою собственную реализацию на PHP, которая была немного более сложной, и избежала некоторых проблем, описанных в комментариях (значительная передача индексов. Если вы перенесете уникальные индексы в таблицу истории, все сломается. Есть решения для это в комментариях). Следование этому посту в письме может быть приключением, в зависимости от того, насколько создана ваша база данных.
Если связь между первичным ключом и столбцом ревизии кажется нарушенной, это обычно означает, что составной ключ каким-то образом заблокирован. В нескольких редких случаях это случалось со мной, и я не мог понять причины.
Я обнаружил, что это решение довольно производительное, поскольку оно использует триггеры. Кроме того, MyISAM быстро выполняет вставку, что и делают все триггеры. Вы можете улучшить это дополнительно с помощью интеллектуальной индексации (или отсутствия ...). Вставка одной строки в таблицу MyISAM с первичным ключом не должна быть операцией, которую вам нужно оптимизировать, на самом деле, если у вас нет серьезных проблем, происходящих в другом месте. За все время, пока я работал с базой данных MySQL, эта реализация таблицы истории использовалась, она никогда не была причиной каких-либо (многих) возникающих проблем с производительностью.
если вы получаете повторяющиеся вставки, проверьте свой программный уровень на предмет запросов типа INSERT IGNORE. Хммм, сейчас не могу вспомнить, но я думаю, что есть проблемы с этой схемой и транзакциями, которые в конечном итоге терпят неудачу после выполнения нескольких действий DML. По крайней мере, кое-что, о чем нужно знать.
Важно, чтобы поля в таблице истории и таблице данных совпадали. Или, скорее, ваша таблица данных не имеет БОЛЬШЕ столбцов, чем таблица истории. В противном случае запросы вставки / обновления / удаления в таблице данных завершатся ошибкой, когда вставки в таблицы истории помещают столбцы в запрос, которые не существуют (из-за d. * В запросах триггера), и триггер не работает. Было бы здорово, если бы в MySQL было что-то вроде схем-триггеров, в которых вы могли бы изменить таблицу истории, если бы столбцы были добавлены в таблицу данных. Есть ли это в MySQL сейчас? Сейчас я React: P
источник
CREATE TABLE MyDB.data_history as select * from MyDB.data limit 0;
owner
поле, и для обновления я мог бы добавитьupdatedby
поле, но для удаления я не уверен, как я могу это сделать с помощью триггеров. обновлениеdata_history
строки с идентификатором пользователя кажется грязным: PВы можете создать триггеры, чтобы решить эту проблему. Вот как это сделать (ссылка в архиве).
Другое решение - сохранить поле «Редакция» и обновить его при сохранении. Вы можете решить, что max - это самая новая ревизия, или что 0 - это самая последняя строка. Решать вам.
источник
Вот как мы это решили
таблица пользователей выглядела так
И бизнес-требования изменились, и нам нужно было проверить все предыдущие адреса и номера телефонов, которые когда-либо были у пользователя. новая схема выглядит так
Чтобы найти текущий адрес любого пользователя, мы ищем UserData с ревизией DESC и LIMIT 1.
Чтобы получить адрес пользователя между определенным периодом времени, мы можем использовать created_on bewteen (date1, date 2)
источник
revision=1
изid_user=1
? Сначала я подумал, что ваш подсчет был,0,2,3,...
но потом я увидел, чтоid_user=2
подсчет исправлений0,1, ...
id
иid_user
столбцы. Just use a group ID of
id` (идентификатор пользователя) иrevision
.MariaDB поддерживает управление версиями системы, начиная с 10.3. Это стандартная функция SQL, которая делает именно то, что вы хотите: хранит историю записей таблиц и предоставляет доступ к ней через
SELECT
запросы. MariaDB - это открытая разработка MySQL. Вы можете узнать больше об управлении версиями системы по этой ссылке:https://mariadb.com/kb/en/library/system-versioned-tables/
источник
Почему бы просто не использовать файлы журналов bin? Если репликация настроена на сервере Mysql, а формат файла binlog установлен на ROW, то все изменения могут быть зафиксированы.
Можно использовать хорошую библиотеку Python под названием noplay. Больше информации здесь .
источник
Только мои 2 цента. Я бы создал решение, которое точно записывает, что изменилось, очень похоже на временное решение.
Моя таблица изменений была бы простой:
DateTime | WhoChanged | TableName | Action | ID |FieldName | OldValue
1) Когда в основной таблице изменяется вся строка, в эту таблицу попадет много записей, НО это очень маловероятно, поэтому не большая проблема (люди обычно меняют только одну вещь) 2) OldVaue (и NewValue, если вы want) должны быть своего рода эпическим "любым типом", поскольку это могут быть любые данные, может быть способ сделать это с помощью типов RAW или просто использовать строки JSON для преобразования в и из.
Минимальное использование данных, хранит все, что вам нужно, и может использоваться для всех таблиц сразу. Я сам изучаю это прямо сейчас, но, возможно, я так и поступлю.
Для создания и удаления только идентификатор строки, поля не требуются. При удалении флага на основной таблице (активной?) Было бы хорошо.
источник
Прямой способ сделать это - создать триггеры для таблиц. Установите некоторые условия или методы сопоставления. Когда происходит обновление или удаление, он автоматически вставляется в таблицу изменений.
Но самое главное, что если у нас будет много столбцов и много таблиц. Мы должны ввести имя каждого столбца каждой таблицы. Очевидно, это пустая трата времени.
Чтобы справиться с этим более эффективно, мы можем создать несколько процедур или функций для получения имен столбцов.
Мы также можем использовать сторонний инструмент, просто чтобы сделать это. Здесь я пишу Java-программу Mysql Tracker
источник
create table like table
я думаю, легко копирует все столбцы