一、查询魔术 步1:在会话17中发布如下声明: 17> var x refcursor 17> exec open :x for select substr(c,1,5),id from t8; PL/SQL 过程已成功完成。 步2:在会话13删除T8的所有记录且提交: 13> delete t8 ; 已删除 10 行。 13> commit; 提交完成。 步3:在会话17中输出游标X的所有行: 17> print x SUBSTR(C,1 ID ---------- ---------- g 7 h 8 i 9 J 10 AA 1 B 2 C 3 D 4 E 5 f 6 已选择10行。 我们在会话13中已删除且已提交,但在会话17中还是显示出了T8中的所有行,这是为什么呢? 1) 在步1打开游标,但此时并没有执行、执取,在会话17的PGA中,将会开辟一块内存存贮此游标。在游标的相关信息中,有一条就是游标打开是的SCN,假若此时的SCN是1000。 2) 在步2中对表T8进行了操作,并提交。Oracle每个数据块的头部,都记有此块当前的SCN值,此SCN值随着对块的更新而变化。假如,我们在步2更新T8表时,SCN是1200,这个值将被记入块头部的SCN值。 3) 在步3我们发布Print x声明,此声明将完成执行、抓取等步骤。在从T8表的相关块中抓取行时,Oracle会将打开游标时的SCN和块本身的SCN相比较,如果发现后者大于前者,证明打开游标是在更新表之前进行的,Oracle将会在回滚段中寻找小于游标SCN的信息,构造一个在打开游标之前的块,这就是CR块了。如果回滚段中的T8表的信息已经被覆盖,将会报告一个ORA-01555 快照太老错误。 执行时间过长的查询,有时也会报出这个错误。 二、SCN SCN就是Oracle内部的一个记时机值,也就是Oracle的内部时钟。在9i之后,我们可以通过如下的方式获得当前的SCN号: scott@MYTWO> select scn,to_char(scn,’xxxxxxx’) from (select dbms_flashback.get_system_change_number SCN from dual); SCN ---------- 8727805 在几乎所有的地方,你都能见到SCN。在事务表、回滚块中的回滚记录、数据块、日志文件等等,我们上面曾提到过,在游标中,记录的也有游标打开时的SCN。 (在eygle的深入浅出oracle中有关SCN号更详细的介绍,18页) 三、构造CR块 Oracle会在三种情况下去读回滚段构造CR块: 1. 只要数据块上有锁,Oracle将会构造CR块。 2. 游标的SCN小于块的SCN,证明块在游标打开之后又被修改过了,这就要去构造CR块。 3. 闪回查询中,要求的SCN小于块的SCN,也要去构造CR块。
构造CR块 步1:先观察表T8的行分布情况: ROWID BLOCK# C ID ------------------ ---------- ---------- ---------- AAAB3LAAFAAAf/mAAA 131046 a 1 AAAB3LAAFAAAf/mAAB 131046 b 2 AAAB3LAAFAAAf/mAAC 131046 c 3 AAAB3LAAFAAAf/nAAA 131047 d 4 AAAB3LAAFAAAf/nAAB 131047 e 5 AAAB3LAAFAAAf/nAAC 131047 f 6 AAAB3LAAFAAAf/oAAA 131048 g 7 AAAB3LAAFAAAf/oAAB 131048 h 8 AAAB3LAAFAAAf/oAAC 131048 i 9 AAAB3LAAFAAAf/pAAA 131049 j 10 步2:发布更新命令 13> update t8 set c=upper(c) where id<=2; 已更新2行。 13> commit; 提交完成。 13> update t8 set c='A1' where id<=1; 已更新 1 行。 13> update t8 set c='A2' where id<=2; 已更新2行。 13> update t8 set c='B2' where id<=2; 已更新2行。 13> commit; 提交完成。 换到其他会话, 17> update t8 set c='aa1' where id<=1; 已更新 1 行。 17> update t8 set c='bb2' where id=2; 已更新 1 行。 17> commit; 提交完成。 17> update t8 set c='aaa1' where id<=1; 已更新 1 行。 17> update t8 set c='bbb2' where id=2; 已更新 1 行。 17> update t8 set c='aaaa1' where id<=1; 已更新 1 行。 17> update t8 set c='bbbb2' where id=2; 已更新 1 行。 17> commit; 提交完成。 再换个会话: 10> update t8 set c='aaaaa10' where id=1; 已更新 1 行。 10> update t8 set c='bbbbb10' where id=2; 已更新 1 行。 10> commit; 提交完成。 10> update t8 set c='az' where id=1; 已更新 1 行。 10> update t8 set c='by' where id=2; 已更新 1 行。 此时的SCN是 18> select dbms_flashback.get_system_change_number SCN from dual; SCN ---------- 8729612 总结一下 行1的变化a->A->A1 A2 B2->aa1->aaa1 aaaa1->aaaaa10->az 行2的变化b->B->B2 ->bb2->bbb2 bbbb2->bbbbb10->by
|