資料回滾:基於時間的查詢(AS OF TIMESTAMP)和(AS OF ACN)
仍以前文中建立的表為例,既然是基於SCN的查詢,我們首先就需要得到SCN,這裡我們通過DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER函式來獲取Oracle當前的SCN,之後再執行資料的修改操作
提 示
如何獲取Oracle資料庫當前的SCN?
獲取當前SCN的方式非常多,除了使用DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER函式外,也可以通過查詢V$DATABASE檢視中的CURRENT_SCN列獲取。不過,不管是通過查詢檢視,或是通過過程獲取,操作的使用者都必須擁有要操作物件的訪問許可權。
例如,授予使用者使用DBMS_FLASHBACK包的許可權:
- JSSPRE> CONN/AS SYSDBA
- Connected.
- JSSPRE> GRANT EXECUTE ON DBMS_FLASHBACK TO SCOTT;
- Grant succeeded.
又如,授予使用者查詢V$DATABASE檢視的許可權:
- JSSPRE> CONN/AS SYSDBA
- Connected.
- JSSPRE> GRANT SELECT ON V_$DATABASE TO SCOTT;
- Grant succeeded.
使用DBMS_FLASHBACK包獲取當前的SCN,然後執行刪除操作並提交:
-
JSSPRE> SELECT DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER
- GET_SYSTEM_CHANGE_NUMBER
- ------------------------
- 1257245
- JSSPRE> DELETE FLASH_TBL WHERE ID>10;
- 10 rows deleted.
- JSSPRE> COMMIT;
- Commit complete.
執行SELECT語句並附加AS OF SCN子句,同時指定刪除前的SCN,就可以查詢到指定SCN時物件中的記錄:
- JSSPRE> SELECT * FROM FLASH_TBL AS OF SCN 1257245;
- ID VL
- ---------- --
-
10 K
- 11 L
- 12 M
- 13 N
- 14 O
- 15 P
- 16 Q
- 17 R
- 18 S
- 19 T
- 20 U
- 1 A
- 2 B
- 3 C
- 4 D
- 5 E
- 6 F
- 7 G
- 8 H
- 9 I
- 20 rows selected.
執行INSERT,將刪除的資料重新恢復回表JSS_TB1:
- JSSPRE> INSERT INTO FLASH_TBL SELECT * FROM FLASH_TBL
- AS OF SCN 1257245 WHERE ID>10;
- 10 rows created.
- JSSPRE> commit;
- Commit complete.
使用SCN查詢會比TIMESTAMP更加精確,事實上,即使執行Flashback Query時指定的是AS OF TIMESTAMP,Oracle也會將其轉換成SCN,這是由於Oracle內部都是通過SCN來標記操作而不是時間。
不過在實際執行Flashback Query時,時間轉換後具體對應哪個SCN,是通過SYS使用者下的一個數據字典實現的,即SMON_SCN_TIME(時間與SCN的對映關係表):
- JSSPRE> DESC SYS.SMON_SCN_TIME;
- Name Null? Type
- ------------------ -------- ---------------
- THREAD NUMBER
- TIME_MP NUMBER
- TIME_DP DATE
- SCN_WRP NUMBER
- SCN_BAS NUMBER
- NUM_MAPPINGS NUMBER
- TIM_SCN_MAP RAW(1200)
- SCN NUMBER
- ORIG_THREAD NUMBER
在10g中,系統平均每3秒產生一次系統時間與SCN的匹配並存入SYS.SMON_SCN_ TIME表,因此10g版本如果使用AS OF TIMESTAMP查詢UNDO中的資料,實際獲取的資料是以指定的時間對應的SCN時的資料為基準。
舉個例子,如SCN:339988,339989分別匹配2009-05-30 13:52:00和2009-05-30 13:57:00,則當你通過AS OF TIMESTAMP查詢2009-05-30 13:52:00或2009-05-30 13:56:59這段時間點內的任何時間,oracle都會將其匹配為SCN:339988到UNDO表空間中查詢,也就說在這個時間內,不管你指定的時間點是什麼,查詢返回的都將是2009-05-30 13:52:00這個時刻對應的SCN的資料。如果通過上述文字的描述仍覺得不夠形象,我想你親自執行一下SELECT SCN,TO_CHAR(TIME_DP,'YYYY-MM-DD HH24:MI:SS')FROM SYS.SMON_ SCN_TIME,會理解得更深刻一些。
在Oracle資料庫中也可以手動進行時間和SCN的相互轉換,Oracle提供了兩個函式SCN_TO_TIMESTAMP和TIMESTAMP_TO_SCN專門幹這個,例如:
- JSSPRE> SELECT TIMESTAMP_TO_SCN(SYSDATE) FROM DUAL;
- TIMESTAMP_TO_SCN(SYSDATE)
- -------------------------
- 1263291
- JSSPRE> SELECT TO_CHAR(SCN_TO_TIMESTAMP(1263291),
- 'YYYY-MM-DD') FROM DUAL;
- TO_CHAR(SC
- ----------
- 2009-06-02
提 示
上面的示例中TIMESTAMP型別經過TO_CHAR格式化,只顯示了日期,千萬別以為只能精確到日期喲。Oracle中的TIMESTAMP日期型別最大能夠精確到納秒(不過一般作業系統返回的精度只到毫秒,因此即使格式化顯示出納秒的精度也沒意義,毫秒後就全是零了)。
看起來很強大吧?其實這兩個函式的轉換依賴於SYS.SMON_SCN_TIME表,能夠轉換到的最小SCN,也正是這個表中的最小記錄,例如:
- JSSPRE> SELECT SCN_WRP*4294967296+SCN_BAS FROM SYS.SMON_SCN_TIME
- 2 WHERE TIME_MP=(SELECT MIN(TIME_MP) FROM SYS.SMON_SCN_TIME);
- SCN_WRP*4294967296+SCN_BAS
- --------------------------
- 554140
能夠轉換到的最小SCN值是554140,使用SCN_TO_TIMESTAMP查詢該SCN對應的時間:
- JSSPRE> SELECT SCN_TO_TIMESTAMP(554140) FROM DUAL;
- SCN_TO_TIMESTAMP(554140)
- ---------------------------------------------------------------------------
- 21-MAR-09 11.51.30.000000000 PM
比該SCN哪怕只再小1的值也無法轉換了,因為SYS.SMON_SCN_TIME表中沒有對應的對映關係:
- JSSPRE> SELECT SCN_TO_TIMESTAMP(554139) FROM DUAL;
- select scn_to_timestamp(554139) from dual
- *
- ERROR at line 1:
- ORA-08181: specified number is not a valid system change number
- ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1
時間的轉換也是同理,所以你看,如果SYS.SMON_SCN_TIME表中不存在時間和SCN的對映關係,則執行函式轉換時就會報錯,也就是說時間和SCN之間並不存在絕對的對應關係。一切都是Oracle提供給你的,只有當它願意讓你看,你才能夠看到。