1. 程式人生 > >資料回滾:基於時間的查詢(AS OF TIMESTAMP)和(AS OF ACN)

資料回滾:基於時間的查詢(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包的許可權:

  1. JSSPRE> CONN/AS SYSDBA
  2. Connected.
  3. JSSPRE> GRANT EXECUTE ON DBMS_FLASHBACK TO SCOTT;
  4. Grant succeeded.

又如,授予使用者查詢V$DATABASE檢視的許可權:

  1. JSSPRE> CONN/AS SYSDBA
  2. Connected.
  3. JSSPRE> GRANT SELECT ON V_$DATABASE TO SCOTT;
  4. Grant succeeded.

使用DBMS_FLASHBACK包獲取當前的SCN,然後執行刪除操作並提交:

  1. JSSPRE> SELECT DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER
     FROM DUAL;
  2. GET_SYSTEM_CHANGE_NUMBER
  3. ------------------------
  4. 1257245
  5. JSSPRE> DELETE FLASH_TBL WHERE ID>10;
  6. 10 rows deleted.
  7. JSSPRE> COMMIT;
  8. Commit complete.

執行SELECT語句並附加AS OF SCN子句,同時指定刪除前的SCN,就可以查詢到指定SCN時物件中的記錄:

  1. JSSPRE> SELECT * FROM FLASH_TBL AS OF SCN 1257245;
  2. ID VL
  3. ---------- --
  4. 10 K
  5. 11 L
  6. 12 M
  7. 13 N
  8. 14 O
  9. 15 P
  10. 16 Q
  11. 17 R
  12. 18 S
  13. 19 T
  14. 20 U
  15. 1 A
  16. 2 B
  17. 3 C
  18. 4 D
  19. 5 E
  20. 6 F
  21. 7 G
  22. 8 H
  23. 9 I
  24. 20 rows selected.

執行INSERT,將刪除的資料重新恢復回表JSS_TB1:

  1. JSSPRE> INSERT INTO FLASH_TBL SELECT * FROM FLASH_TBL
  2. AS OF SCN 1257245 WHERE ID>10;
  3. 10 rows created.
  4. JSSPRE> commit;
  5. Commit complete.

使用SCN查詢會比TIMESTAMP更加精確,事實上,即使執行Flashback Query時指定的是AS OF TIMESTAMP,Oracle也會將其轉換成SCN,這是由於Oracle內部都是通過SCN來標記操作而不是時間。

不過在實際執行Flashback Query時,時間轉換後具體對應哪個SCN,是通過SYS使用者下的一個數據字典實現的,即SMON_SCN_TIME(時間與SCN的對映關係表):

  1. JSSPRE> DESC SYS.SMON_SCN_TIME;
  2. Name Null? Type
  3. ------------------ -------- ---------------
  4. THREAD NUMBER
  5. TIME_MP NUMBER
  6. TIME_DP DATE
  7. SCN_WRP NUMBER
  8. SCN_BAS NUMBER
  9. NUM_MAPPINGS NUMBER
  10. TIM_SCN_MAP RAW(1200)
  11. SCN NUMBER
  12. 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專門幹這個,例如:

  1. JSSPRE> SELECT TIMESTAMP_TO_SCN(SYSDATE) FROM DUAL;
  2. TIMESTAMP_TO_SCN(SYSDATE)
  3. -------------------------
  4. 1263291
  5. JSSPRE> SELECT TO_CHAR(SCN_TO_TIMESTAMP(1263291),
  6. 'YYYY-MM-DD') FROM DUAL;
  7. TO_CHAR(SC
  8. ----------
  9. 2009-06-02

提 示

上面的示例中TIMESTAMP型別經過TO_CHAR格式化,只顯示了日期,千萬別以為只能精確到日期喲。Oracle中的TIMESTAMP日期型別最大能夠精確到納秒(不過一般作業系統返回的精度只到毫秒,因此即使格式化顯示出納秒的精度也沒意義,毫秒後就全是零了)。

看起來很強大吧?其實這兩個函式的轉換依賴於SYS.SMON_SCN_TIME表,能夠轉換到的最小SCN,也正是這個表中的最小記錄,例如:

  1. JSSPRE> SELECT SCN_WRP*4294967296+SCN_BAS FROM SYS.SMON_SCN_TIME
  2. 2 WHERE TIME_MP=(SELECT MIN(TIME_MP) FROM SYS.SMON_SCN_TIME);
  3. SCN_WRP*4294967296+SCN_BAS
  4. --------------------------
  5. 554140

能夠轉換到的最小SCN值是554140,使用SCN_TO_TIMESTAMP查詢該SCN對應的時間:

  1. JSSPRE> SELECT SCN_TO_TIMESTAMP(554140) FROM DUAL;
  2. SCN_TO_TIMESTAMP(554140)
  3. ---------------------------------------------------------------------------
  4. 21-MAR-09 11.51.30.000000000 PM

比該SCN哪怕只再小1的值也無法轉換了,因為SYS.SMON_SCN_TIME表中沒有對應的對映關係:

  1. JSSPRE> SELECT SCN_TO_TIMESTAMP(554139) FROM DUAL;
  2. select scn_to_timestamp(554139) from dual
  3. *
  4. ERROR at line 1:
  5. ORA-08181: specified number is not a valid system change number
  6. ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1

時間的轉換也是同理,所以你看,如果SYS.SMON_SCN_TIME表中不存在時間和SCN的對映關係,則執行函式轉換時就會報錯,也就是說時間和SCN之間並不存在絕對的對應關係。一切都是Oracle提供給你的,只有當它願意讓你看,你才能夠看到。