在事务数据库中撤消

我不知道如何使用事务数据库实现用户友好界面的撤销属性。 一方面,建议用户具有多级(无限)撤销可能性,如答案中所述。可能有助于此问题的模式是Memento或Command。 但是,使用包含触发器,不断增长的序列号和不可逆转的过程的复杂数据库,很难想象撤消操作如何在不同于事务边界的点处起作用。 换句话说,撤消到最后一次提交的事务只是回滚时,但是如何回到不同的时刻? 更新(基于到目前为止的答案):我不一定希望撤消在修改已经提交时有效,我将专注于具有开放事务的正在运行的应用程序。每当用户点击保存时,它意味着提交,但在保存之前 - 在同一事务期间 - 撤消应该有效。我知道使用数据库作为持久层只是一个实现细节,用户不应该为此烦恼。但是,如果我们认为“在数据库和GUI中撤消的想法是根本不同的东西”,并且我们不对数据库使用撤销,那么无限撤消只是一个流行语。 我知道“回滚是......不是用户撤消”。 那么如何在同一个事务中给出“由于任何更改而产生的级联效应”,实现客户端级撤销?     
已邀请:
在数据库和GUI中撤消的想法是根本不同的事情; GUI将成为单个用户应用程序,与其他组件的交互水平较低;数据库是一个多用户应用程序,其中更改可能会因任何更改而产生级联效果。 要做的是允许用户尝试将以前的状态应用为新事务,这可能会也可能不会起作用;或者只是在提交后没有撤消(类似于保存后没有撤消,这是许多应用程序中的一个选项)。     
一些(所有?)DBMS支持保存点,允许部分回滚:
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语句中),因此可以: 知道谁修改了哪个字段以及何时:保罗在形式发票中插入新行,Bill重新关联商品的单价,Pat修改最终订购数量等) 使用以下规则“撤消”以前的所有交易: 'INSERT'撤消是基于唯一标识符的'DELETE' 'UPDATE'撤消相当于之前的'UPDATE' 'DELETE'撤消与第一个INSERT相同,然后是所有更新 因为我们没有跟踪 超过3个月的交易, UNDO并非总是可用。 对这些功能的访问仅限于数据库管理员,因为不允许其他用户在业务规则之外进行任何数据更新(例如:一旦采购订单,采购订单行上的'撤消'的含义是什么已经得到供应商的同意?)。说实话,我们很少使用这个选项(一年几次?)     
它几乎与William的帖子(我实际上已经投票)相同,但我试着指出更详细一点,为什么需要实现用户撤销(与使用数据库回滚相比)。 了解有关您的应用程序的更多信息会很有帮助,但我认为对于用户(友好)撤销/重做数据库并不是实现该功能的适当层。 用户想要撤消他所做的操作,而不管这些操作是否导致没有/一个/多个数据库事务 用户想要撤消他所做的动作(不是其他任何人) 从我的角度来看,数据库是实现细节,一种工具,用作存储数据的程序员。回滚是一种帮助您这样做的撤消,它不是用户撤消。使用回滚意味着让用户参与他不想知道但不理解(并且不必理解)的事情,这从来都不是一个好主意。 正如William发布的那样,您需要在客户端或服务器端内部实现作为会话的一部分,该会话跟踪您定义为用户事务的步骤并能够撤消这些步骤。如果在这些用户事务期间进行了数据库事务,则需要其他数据库事务来撤销这些事务(如果可能)。如果无法撤消,请确保提供有价值的反馈,这再次意味着,在业务方面解释它而不是数据库方式。     
要保持任意回滚到先前的语义,您需要在数据库中实现逻辑删除。其工作原理如下: 每条记录都有一个“已删除”标志,版本号和/或“当前指标”标志,具体取决于您需要重建的聪明程度。此外,您需要跨该实体的所有版本使用每个实体密钥,以便您知道哪些记录实际引用了哪个特定实体。如果您需要知道某个版本适用的时间,您还可以使用“发件人”和“收件人”列。 删除记录时,将其标记为“已删除”。更改它时,您将创建一个新行并更新旧行以反映其过时。使用版本号,您只需找到要回滚的上一个号码即可。 如果您需要参照完整性(您可能会这样做,即使您认为不这样做)并且可以处理额外的I / O,您还应该有一个父表,其中密钥作为所有版本记录的占位符。 在Oracle上,集群表对此非常有用;父表和版本表都可以共存,从而最大限度地减少I / O的开销。在SQL Server上,包含密钥的覆盖索引(可能集群在实体密钥上)将减少额外的I / O.     

要回复问题请先登录注册