2015-05-12 73 views
0

我讀過Oracle全局臨時表中的保存點刪除所有數據,但是當我在Oracle 11g上測試時,它們像堆表一樣工作。有人可以解釋嗎?Oracle全局臨時表中的保存點

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

你是從哪裏讀到的?我懷疑不在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狀態:

「的臨時表定義相同的方式,普通表的定義仍然存在,」基本上他們堆表。的區別在於:

  • 範圍(可見性)的數據
  • 用於保留數據(全局臨時表寫入到臨時表)的表空間。
0

我認爲你誤會了 - 如果你回滾到一個保存點,那麼Oracle應該撤消保存點之後完成的所有工作(同時仍保留在保存點之前完成的任何未提交的工作)。

對於一個臨時表,當你放入東西時,Oracle會懶惰地分配存儲空間(會話的臨時段),當數據完成時(在會話結束時或在事務,取決於類型),它可以取消分配存儲空間而不是單獨刪除行,而不是像在TRUNCATE普通表時發生的情況那樣。

我很有興趣看看你任何數據被放置在之前有一個保存點發生了什麼事,並回滾到保存點 - 將甲骨文解除分配存儲或將保持存儲和刪除行從內它?

事實證明前者 - 它表現得像截斷。

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),但我認爲這不會影響保存點的行爲。