我认为您误解了 - 如果您回滚到保存点,那么 Oracle 应该撤消保存点之后完成的所有工作(同时仍然保留在保存点之前完成的任何未提交的工作)。
对于临时表,Oracle 会在您放入内容时懒惰地分配存储空间(会话的临时段),并且在数据处理完毕时(在会话结束时或在事务结束时,取决于类型)它可以只释放存储空间而不是单独删除行,就像你截断一个普通表时发生的那样。
我很想知道如果您在放入任何数据之前有一个保存点并回滚到该保存点会发生什么 - Oracle 会取消分配存储还是保留存储并从其中删除行?
事实证明是前者 - 它的行为就像一个截断。
SAVEPOINT f0;
SELECT * FROM v$tempseg_usage; -- Should show nothing for your session
insert into table_1 values('one');
insert into table_1 values('two');
SELECT * FROM v$tempseg_usage; -- Should show a DATA row for your session
savepoint f1;
insert into table_1 values('three');
insert into table_1 values('four');
rollback to f1; -- Undo three and four but preserve one and two
SELECT * FROM v$tempseg_usage; -- Still shows a DATA row for your session
rollback to f0; -- Undo all the inserts
SELECT * FROM v$tempseg_usage; -- row for your session has gone
这很重要的原因是,当您执行正常删除(而不是截断)时,对表的任何完整扫描仍然必须筛选所有数据块以查看它们是否有任何数据。针对空表的 DML 可以如果表在之前的某个时间有大量数据,则可能会产生大量 I/O !
我正在尝试加快一些正在执行此操作的代码 - 它将一些东西作为暂存器放入临时表中,部分是为了它可以加入永久表,并将结果返回给它的调用者。临时表只是为了这个例程的好处,所以在例程结束时清除它是安全的,但是它可能在父事务中被多次调用,所以我不能截断(TRUNCATE
是 DDL 等提交事务),但我也不能不清除它,否则同一事务中的调用将拾取彼此的行。通过 DELETE 清除会导致相当多的开销,特别是因为表上没有索引,因此对其进行选择将始终进行全扫描。
我正在探索的选项是SAVEPOINT
在例程开始时有一个,做我的临时工作,然后在它返回结果之前回滚到保存点。另一种选择可能是将例程放在自治事务中,但这意味着将 C 代码移植到 PL/SQL 存储过程,如果临时表需要连接到调用者插入的未提交数据,则无论如何都不会工作。
请注意,我在 12c 中进行了研究 - 此版本中对临时表进行了一些改进(请参阅https://oracle-base.com/articles/misc/temporary-tables),但我认为这不会影响行为wrt保存点。