在 Oracle(可能还有其他地方)中,执行 ALTER TABLE 语句将对当前事务执行隐式提交。
我们有一个工具(用 Java 编写)应该通过以下方式修改模式:
- 添加一些列
- 删除一些列
- 使用新的模式布局更新描述表
编写的工具将向用户显示当前模式布局并允许他修改它。(基本上在某些表中添加或删除自定义“属性”列)一旦他满意,他就可以应用他的更改。
请注意:基本模式布局,以及您需要 ALTER TABLE 来更改某些内容的事实是由标准预定义的,我们无法更改,因为其他工具将不再起作用。
现在的问题是我们不能在单个事务中运行这些更改,因为 AFAIK,不可能在一个事务中执行多个 ALTER TABLE 语句。
如果在应用更改时出现问题,我们有哪些选项可以“回滚”到初始状态?
注意:这里有人提出了 RESTORE POINT + FLASHBACK ——从这个(Java)工具调用它是个好主意吗?(在某些应该使用该工具的站点,我们不会完全控制数据库实例。)
注:Oracle 10g2 及以上
由于 DDL 隐式提交,“回滚”更改的唯一方法是构造反向操作并将其应用于还原更改,如 a_horse_with_no_name 所述。
然而,构建这样的回滚并不总是那么简单。如果数据可以在类型修改 (varchar2(10) -> varchar2(50), number -> varchar2) 之间写入表并回滚,那么您还必须检查新数据在恢复时是否有效原始类型(或执行一些转换)。请注意,在大型表上删除列可能需要一些时间并生成大量重做。
您还必须警惕由于这些更改而使数据库和其他应用程序依赖项上的任何存储过程无效。
在这种情况下,闪回选项对您没有帮助。对表进行 DDL 更改后,您无法使用闪回将其恢复到之前的状态。尝试这样做会给你错误:
ORA-01466: 无法读取数据 - 表定义已更改
闪回您的完整数据库将是矫枉过正,也无法从 Java 应用程序 - 您需要关闭然后挂载数据库才能完成此操作。
这一切都提出了你的工具是做什么的问题。如果您只需要一个 GUI 供人们编辑表,那么像 Oracle SQL Data Modeler 之类的工具可以完成此操作并为您生成 DDL 脚本。然后可以对这些进行验证、测试、构建适当的回滚并将其应用于数据库。修改(生产)数据库的结构应该小心并经过测试,以确保所有更改都是有效的!
我能想到的唯一方法(不迁移到支持事务 DDL 的 DBMS)是编写自己的“DDL 事务”处理,在其中创建相应的语句来回滚您所做的实际更改。
“回滚” ADD COLUMN 非常简单,因为您只需要删除该列。要回滚 DROP COLUMN,我看到的唯一选择是先重命名该列,然后在一切成功后删除所有重命名的列。要回滚“虚拟删除”,您只需将列重命名为原始名称。
另一种选择可能是在修改它们之前创建表的副本,例如使用
CREATE TABLE backup_table AS SELECT * FROM original_table
(但如果表真的很大,这显然不是一个好的解决方案)使用闪回可能不是很可靠,因为您不能依赖闪回数据的可用性。保证闪回时间的默认值为15 分钟。但是 DBA 可以自由选择较小的值。
Oracle 在 11gR2 中添加了一个基于版本的重新定义功能,用于升级应用程序模式,大部分是在线的。您可能可以使用它来实现您的目标。
对一组临时表进行所有更改。
当一切顺利完成后,将所有更改应用到真实表格。
Oracle DDL 语句隐式提交。这是我使用的唯一DBMS。
Oracle 的“闪回表”可以将表回退到更早的时间点……但它在对该表的结构(即 DDL)更改时 不起作用。
“闪回数据库”使用不同的机制,但会将整个数据库恢复到更改之前,您可能不允许这样做。
另一个没有人提到的选项是DBMS_REDEFINITION单体。 这是一个巨大的、由 Oracle 提供的包,应该为您管理所有这些,使您的表更改透明、无缝并避免 [几乎] 任何服务中断。它很大而且很复杂,但它可能值得投资来掌握它。
我认为您最好添加一个结构,您的客户认为他们正在添加列,但实际上他们只是在表中添加记录,例如:“Table_Column_DEF”。然后,您只需要默认表和“Table_Column_DEF”之间的链接表,您还可以在其中存储值。
使用这种方法,您的客户可以添加他们想要的任何列,并且可以在 1 个事务中完成此操作。您可以从回滚、闪回等中受益……您当然需要考虑一些权衡:
1) 价值栏:
2) 性能
3)查询:您必须动态构建它。循环遍历所有“Table_Column_DEF”记录并使用“选择(从 LinkTable 中选择 Value_Int where ...)Value_Int,... from ...”添加它们