我读到Oracle全局临时表中的保存点会删除所有数据,但是当我在Oracle 11g上测试时,它们就像堆表一样工作。有人可以解释一下吗?
insert into table_1 values('one');
insert into table_1 values('two');
savepoint f1;
insert into table_1 values('three');
insert into table_1 values('four');
rollback to f1;
-- the records in table are 2 records just like heap tables, but I read that
-- savepoints in GTT truncates all the data
答案 0 :(得分:0)
你在哪里读到这个?我怀疑不在Oracle SQL Reference中。所以解释很简单:该断言的作者没有测试全局临时表的行为。或者您正在阅读其他一些SQL实现的描述,例如DerbyDB。
为了完整起见,让我们排除交易或会话范围的作用。这是两个全局临时表:
create global temporary table gtt1
( col1 varchar2(30) )
ON COMMIT PRESERVE ROWS
/
create global temporary table gtt2
( col1 varchar2(30) )
ON COMMIT DELETE ROWS
/
让我们为具有会话范围的实验运行您的实验:
SQL> insert into gtt1 values('one');
1 row created.
SQL> insert into gtt1 values('two');
1 row created.
SQL> savepoint f1;
Savepoint created.
SQL> insert into gtt1 values('three');
1 row created.
SQL> insert into gtt1 values('four');
1 row created.
SQL> rollback to f1;
Rollback complete.
SQL> select * from gtt1;
COL1
------------------------------
one
two
SQL>
具有事务范围的表的结果相同:
SQL> insert into gtt2 values('five');
1 row created.
SQL> insert into gtt2 values('six');
1 row created.
SQL> savepoint f2;
Savepoint created.
SQL> insert into gtt2 values('seven');
1 row created.
SQL> insert into gtt2 values('eight');
1 row created.
SQL> rollback to f2;
Rollback complete.
SQL> select * from gtt2;
COL1
------------------------------
five
six
SQL>
实际上这并不奇怪。 official Oracle documentation州:
“临时表定义的持续方式与常规表的定义相同”
基本上他们是堆表。不同之处是:
答案 1 :(得分:0)
我认为您误解了 - 如果您回滚到保存点,那么Oracle应该撤消保存点之后完成的所有工作(同时仍然保留在保存点之前完成的任何未提交的工作)。
对于临时表,当您放入内容时,Oracle会懒惰地分配存储(会话的临时段),并且当数据完成时(在会话结束时或在事务结束时) ,取决于类型)它可以释放存储而不是单独删除行,而不是像TRUNCATE普通表时发生的那样。
我有兴趣了解如果在之前有一个保存点放入任何数据并回滚到该保存点会发生什么 - 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,因此提交事务)但我也不能清除它,或者同一事务中的调用将获取一个anothers'行。通过DELETE清除会导致相当大的开销,特别是因为表上没有索引,因此选择它将始终进行全扫描。
我正在探索的选项是在例程开始时使用SAVEPOINT
,进行临时工作,然后在返回结果之前回滚到保存点。另一种选择可能是将例程放在自治事务中,但这意味着将C代码移植到PL / SQL存储过程,如果临时表需要连接到调用者插入的未提交数据,则无论如何都无法工作。
请注意,我在12c中完成了我的研究 - 在此版本中对临时表进行了一些改进(请参阅https://oracle-base.com/articles/misc/temporary-tables),但我认为这不会影响保存点的行为。