Отменить в транзакционной базе данных

Я не знаю, как реализовать свойство отмены дружественных интерфейсов с использованием транзакционной базы данных.

С одной стороны, желательно, чтобы пользователь имел многоуровневую (бесконечную) возможность удаления, как указано здесь в ответе. Паттернами, которые могут помочь в этой проблеме, являются Memento или Command.

Однако, используя сложную базу данных, включающую триггеры, постоянно растущие порядковые номера и необратимые процедуры, трудно представить, как действие по отмене может работать в разных точках, чем границы транзакции. Другими словами, отменить до того момента, когда транзакция, совершенная в последний раз, является просто откатом, но как можно вернуться к другим моментам?

ОБНОВЛЕНИЕ (основано на ответах до сих пор): я не обязательно хочу, чтобы отмена работала, когда изменение уже зафиксировано, я бы сосредоточился на работающем приложении с открытой транзакцией. Всякий раз, когда пользователь нажимает кнопку сохранения, это означает фиксацию, но перед сохранением - во время той же транзакции - отмена должна работать. Я знаю, что использование базы данных в качестве постоянного слоя - это просто деталь реализации, и пользователь не должен беспокоиться об этом. Но если мы думаем, что "идея отмены в базе данных и в графическом интерфейсе - это принципиально разные вещи", и мы не используем отмену в базе данных, то бесконечное удаление - просто модное слово. Я знаю, что "откат - это не отмена пользователя".

Итак, как реализовать отмену на уровне клиента с учетом "каскадных эффектов в результате любых изменений" внутри одной и той же транзакции?

5 ответов

Идея отмены в базе данных и в графическом интерфейсе - это принципиально разные вещи; GUI будет однопользовательским приложением с низким уровнем взаимодействия с другими компонентами; база данных - это многопользовательское приложение, в котором изменения могут иметь каскадный эффект в результате любого изменения.

Что нужно сделать, это позволить пользователю попытаться применить предыдущее состояние в качестве новой транзакции, которая может или не может работать; или, альтернативно, просто не отменять действия после коммита (аналогично отмене операций после сохранения, что является опцией во многих приложениях).

Некоторые (все?) СУБД поддерживают точки сохранения, которые допускают частичные откаты:

savepoint s1;
insert into mytable (id) values (1);
savepoint s2;
insert into mytable (id) values (2);
savepoint s3;
insert into mytable (id) values (3);
rollback to s2;
commit;

В приведенном выше примере останется только первая вставка, остальные две будут отменены.

Я не думаю, что в целом практично пытаться отменить после коммита, по причинам, которые вы указали * и, возможно, другим. Если это важно в каком-то сценарии, вам придется создать много кода для этого, а также учесть влияние триггеров и т. Д.

  • Я не вижу никаких проблем с постоянно увеличивающимися последовательностями, хотя?

Мы разработали такую ​​возможность в нашей базе данных, отслеживая все транзакции, примененные к данным (на самом деле не все, а только те, которым менее 3 месяцев). Основная идея состояла в том, чтобы видеть, кто что и когда делал. Каждая запись в базе данных, уникально идентифицируемая по ее GUID, может затем рассматриваться как результат одного оператора INSERT, нескольких операторов UPDATE и, наконец, одного оператора DELETE. Поскольку мы отслеживаем все эти операторы SQL и поскольку INSERT являются глобальными INSERTS (отслеживание значения всех полей сохраняется в операторе INSERT), то становится возможным:

  • Знайте, кто изменил какое поле и когда: Пол вставил новую строку в счет-проформу, Билл пересмотрел цену за единицу товара, Пэт изменил окончательное заказанное количество и т. Д.)
  • "отменить" все предыдущие транзакции по следующим правилам:

    отмена "INSERT" - это "DELETE" на основе уникального идентификатора

    отмена "ОБНОВЛЕНИЕ" эквивалентна предыдущему "ОБНОВЛЕНИЕ"

    Отмена 'DELETE' эквивалентна первой INSERT, за которой следуют все обновления

  • Поскольку мы не отслеживаем транзакции старше 3 месяцев, UNDO доступны не всегда.

Доступ к этим функциям строго ограничен только менеджерами баз данных, поскольку другим пользователям не разрешается обновлять данные вне бизнес-правил (например: что означает значение "отменить" в строке "Заказ на покупку" после заказа на покупку) был согласован с поставщиком?). Честно говоря, мы используем эту опцию очень редко (несколько раз в год?)

Это почти то же самое, что и пост Уильяма (за который я на самом деле проголосовал), но я попытаюсь указать немного более подробно, почему необходимо реализовать отмену действий пользователя (по сравнению с использованием отката базы данных).

Было бы полезно узнать больше о вашем приложении, но я думаю, что для пользователя (дружественного) Отменить / Повторить база данных не является адекватным уровнем для реализации этой функции.

  1. Пользователь хочет отменить действия, которые он совершил, независимо от того, ведут ли они к одной / нескольким транзакциям базы данных.
  2. Пользователь хочет отменить те действия, которые он сделал (а не кто-либо другой)

С моей точки зрения, база данных - это детали реализации, инструмент, который вы используете как программист для хранения данных. Откат - это своего рода отмена, которая помогает ВАМ в этом, это не пользовательская отмена. Использование отката означало бы вовлечение пользователя в вещи, о которых он не хочет знать и не понимает (и не должен), что никогда не было бы хорошей идеей.

Как писал Уильям, вам нужна реализация внутри клиента или на стороне сервера как часть сеанса, которая отслеживает этапы того, что вы определяете как пользовательскую транзакцию, и может отменить их. Если транзакции базы данных были сделаны во время этих пользовательских транзакций, вам нужны другие транзакции БД, чтобы отозвать их (если это возможно). Обязательно предоставьте ценную обратную связь, если отмена невозможна, что снова означает, объясните это с точки зрения бизнеса, а не базы данных.

Чтобы поддерживать произвольную семантику отката к предыдущей версии, вам потребуется реализовать логическое удаление в вашей базе данных. Это работает следующим образом:

У каждой записи есть флаг "Удалено", номер версии и / или флаг "Текущий индикатор", в зависимости от того, насколько умной должна быть ваша реконструкция. Кроме того, вам нужен ключ для каждой сущности во всех версиях этой сущности, чтобы вы знали, какие записи фактически ссылаются на какую-то конкретную сущность. Если вам необходимо узнать, когда была применена версия, вы также можете иметь столбцы "От" и "До".

Когда вы удаляете запись, вы помечаете ее как "удаленную". Когда вы изменяете его, вы создаете новую строку и обновляете старую строку, чтобы отразить ее устаревание. С номером версии вы можете просто найти предыдущий номер для отката.

Если вам нужна ссылочная целостность (что вы, вероятно, делаете, даже если вы думаете, что нет) и вы можете справиться с дополнительным вводом / выводом, у вас также должна быть родительская таблица с ключом в качестве заполнителя для записи во всех версиях.

В Oracle кластерные таблицы полезны для этого; и родительская таблица, и таблица версий могут быть совмещены, что минимизирует накладные расходы на ввод / вывод. На SQL Server индекс покрытия (возможно, кластеризованный на ключе сущности), включая ключ, уменьшит дополнительный ввод-вывод.

Другие вопросы по тегам