💫《博主主頁》:
🔎 CSDN主頁__奈斯DB
🔎 IF Club社區主頁__奈斯、
🔥《擅長領域》:擅長阿里云AnalyticDB for MySQL(分布式數據倉庫)、Oracle、MySQL、Linux、prometheus監控;并對SQLserver、NoSQL(Redis)有了解
💖如果覺得文章對你有所幫助,歡迎點贊收藏加關注💖
????話接上文,在上文中了解了通過ORA_ROWSCN
偽列可以精準定位行數據的最后修改時間?。但是如果有這樣一個場景,不僅想知道行數據被修改的時間,還想知道修改前的舊值,以及這行數據被修改了多少次,需要進行數據追溯,那這個有沒有辦法實現的呢🔍?
????Oracle表示:"當然可以!"? 通過 閃回版本查詢(Flashback Version Query)的偽列 就可以輕松實現:
- VERSIONS_STARTTIME/VERSIONS_STARTSCN - 版本開始時間/SCN
- VERSIONS_ENDTIME/VERSIONS_ENDSCN - 版本結束時間/SCN
- VERSIONS_XID - 創建該版本的事務ID(追蹤!🕵?♂?)
- VERSIONS_OPERATION - 操作類型(I/U/D)🔧
????不過需要注意的是?? ,這個功能依賴 UNDO表空間 中的數據,必須在UNDO_RETENTION
參數保留期內查詢(默認通常15分鐘~24小時),但實際保留時間還取決于UNDO表空間大小📊,那么下面開始版本查詢偽列的介紹。
????在Oracle 12c官方文檔中可查的的偽列總共有 10個 ,對于網上的博客,以及其他學習資料對偽列的介紹都非常少,幾乎沒有大佬去講關于偽列的內容,那么博主將用一個系列去認真介紹一下這 10個 偽列。先介紹一下偽列,偽列也叫虛擬列、特殊列、隱式列,不管怎么稱呼,都泛指偽列。偽列是Oracle中一種特殊的列,它不像普通列那樣存儲在表中,但可像普通列一樣在查詢中進行引用,在查詢時由數據庫動態生成,主要用于獲取行標識、序列值、數據操作相關元數據等特殊信息。需要注意的是偽列只能進行查詢,不能插入、更新或刪除它們的值。偽列類似于無參數的函數,但無參數函數通常對結果集中的每一行返回相同值,而偽列通常為每一行返回不同的值。
對于比較常用的偽列將用獨立的一篇文章介紹,對于不常用的偽列將用一篇文章簡單介紹清楚其用途即可,因為在實際工作中有幾個偽列使用的很頻繁,真的非常有用處,關于偽列的系列文章如下:
- 第一篇:偽列之ORA_ROWSCN:精準查看行數據被修改的最后時間
- 第二篇:偽列之Version Query:全鏈路追蹤行數據變更的所有記錄(不僅僅是被修改的最后時間)(當前篇)
- 第三篇:偽列之Sequence:利用CURRVAL和NEXTVAL實現主鍵自增(含 12c 新特性通過 Identity 列實現主鍵自增)
- 第四篇:偽列之ROWID:快速查找數據行的物理地址
- 第五篇:偽列之ROWNUM:分頁查詢的實現
- 第六篇:偽列之Hierarchical Query(層次查詢)、COLUMN_VALUE(列值提取)、OBJECT_ID(對象標識)、OBJECT_VALUE(對象值訪問)、XMLDATA(XML原始數據)
???
特別說明💥:本篇文章部分理論性知識點均來源于版權歸 Oracle 所有的官方公開文檔手冊,并結合了我個人的解讀和案例演示。若需要調整,請聯系,會盡快處理😄
???
官方文檔對于版本查詢偽列的介紹(Oracle 12c):
Version Query Pseudocolumns
目錄
- 一、版本查詢偽列介紹:
- 二、版本查詢偽列 VS ORA_ROWSCN偽列
- 案例一:創建表,針對多次被修改的某行數據通過版本查詢偽列查看更改時間,以及修改前后的數據
- 案例二:在現存表上使用版本查詢偽列,針對多次被修改的某行數據通過版本查詢偽列查看更改時間,以及修改前后的數據
???
???
一、版本查詢偽列介紹:
????版本查詢偽列僅在閃回版本查詢(Flashback Version Query)中有效,閃回版本查詢是閃回查詢(Flashback Query)中的一種。
????既然版本查詢偽列和閃回功能有關,那么先介紹一下Oracle中的閃回功能。Oracle中有多種閃回功能。包括Flashback Database(閃回數據庫)、Flashback Drop(回收站)、Flashback Query(閃回查詢)、flashback Table(閃回表)、Flashback Data Archive(閃回數據歸檔)。在Flashback Query(閃回查詢)中又分為閃回查詢(Flashback Query)、閃回版本查詢(Flashback Version Query)、閃回事務查詢(Flashback Transaction Query),關于閃回功能在這里不多贅述,官方文檔鏈接參考(Oracle 12c):
19 Using Oracle Flashback Technology ,如下是不同閃回功能的對比矩陣:
功能 依賴組件 / 參數 粒度 保留期 典型用途 Flashback Query Undo數據 / undo_retention 行級 undo_retention 單點數據查看 Flashback Version Query Undo數據 / undo_retention 行版本級 undo_retention 變更歷史追蹤 Flashback Transaction Query Undo+Flashback日志 / undo_retention 事務級 undo_retention 事務分析 Flashback Table Undo數據 / undo_retention 表級 undo_retention 表數據回退 Flashback Drop 回收站 / recyclebin 對象級 空間壓力決定 表刪除恢復 Flashback Database 閃回日志 / db_flashback_retention_target 數據庫級 日志保留策略 數據庫級回滾 Flashback Data Archive 獨立表空間 行級 用戶定義(年) 合規性存檔 ????清楚了Oracle有哪些閃回功能,并且清楚了版本查詢偽列僅在閃回版本查詢(Flashback Version Query)中有效,那么之后的內容圍繞Version Query Pseudocolumns(版本查詢偽列)和閃回版本查詢(Flashback Version Query)進行介紹。
???版本查詢偽列包括如下:
偽列 描述 VERSIONS_STARTSCN 和 VERSIONS_STARTTIME 功能:標識行版本創建時的起始系統變更號(SCN)或時間戳(TIMESTAMP)
說明:該偽列標識數據首次具有行版本中反映的值的時間點。可用于確定Oracle閃回表或Oracle閃回查詢的過去目標時間
特殊值:如果該偽列為NULL,則表示行版本在查詢開始前就已存在VERSIONS_ENDSCN 和 VERSIONS_ENDTIME 功能:標識行版本失效時的SCN或時間戳
特殊值:如果該偽列為NULL,則表示行版本在查詢時仍為當前版本,或者該行對應于刪除操作VERSIONS_XID 功能:創建該行版本的事務標識符(RAW格式的數字) VERSIONS_OPERATION 功能:事務執行的操作類型:
????·'I'表示插入(insertion)
????·'D'表示刪除(deletion)
????·'U'表示更新(update)
說明:行版本反映的是:
????·插入操作后的行(INSERT)
????·刪除操作前的行(DELETE)
????·更新操作影響的行(UPDATE)注意:對于索引鍵的用戶更新操作,Oracle閃回版本查詢可能會將一個UPDATE操作視為兩個操作(DELETE加INSERT),表現為兩個版本行:先是一個’D’操作,隨后是一個’I’操作(通過VERSIONS_OPERATION標識)。
???閃回版本查詢語法介紹:
????使用Oracle閃回版本查詢(Flashback Version Query)可檢索指定時間區間內特定行存在的不同版本。每次執行COMMIT語句時,都會生成一個行版本。需要注意:執行CREATE TABLE語句后,請至少等待15秒再提交任何事務,以確保Oracle閃回版本查詢(Flashback Version Query)能夠正確反映這些事務變更。
????使用SELECT語句的VERSIONS BETWEEN
子句指定Oracle閃回版本查詢(Flashback Version Query)。語法格式如下:SELECT [偽列], 列1, 列2, ...FROM 表名 VERSIONS BETWEEN { SCN | TIMESTAMP } start AND end [WHERE 條件];參數說明:start和end分別代表要查詢時間區間的起始點和結束點表達式。該時間區間為閉區間[包含start和end時刻]。
????Oracle 閃回版本查詢會返回一個數據表,其中包含指定時間區間內存在過的每一個行版本所對應的記錄。該結果表中的每一行都包含描述行版本元數據的偽列,通過這些信息,您可以追溯數據庫中特定變更(可能是錯誤操作)的發生時間及操作方式。需要注意:閃回版本查詢依賴 UNDO表空間 中的數據,必須在
UNDO_RETENTION
參數保留期內查詢(默認通常15分鐘~24小時),但實際保留時間還取決于UNDO表空間大小
???
???
二、版本查詢偽列 VS ORA_ROWSCN偽列
????ORA_ROWSCN偽列用于查看行數據被修改的最后時間,經過上面的介紹清楚了版本查詢偽列主要也是追蹤數據變更,只不過版本查詢偽列可以查到行數據變更的所有記錄(不僅是被修改的最后時間),因此這里簡單介紹一下這兩個在功能和使用場景上有顯著差異:
???版本查詢偽列:
- 用途: 用于閃回版本查詢(Flashback Version Query),追蹤行級數據的歷史變更
- 主要偽列:
- VERSIONS_STARTTIME/VERSIONS_STARTSCN - 版本開始時間/SCN
- VERSIONS_ENDTIME/VERSIONS_ENDSCN - 版本結束時間/SCN
- VERSIONS_XID - 創建該版本的事務ID
- VERSIONS_OPERATION - 操作類型(I/U/D)
- 特點:
- 需要顯式使用 VERSIONS BETWEEN 語法
- 提供完整的行變更歷史記錄
- 可以查看中間版本(而不僅是最后修改)
- 依賴于UNDO數據,有時間限制
???
ORA_ROWSCN偽列:
- 用途: 提供行最后修改的SCN(系統變更號)
- 特點:
- 直接附加在普通查詢中
- 每行一個SCN值(最后修改的SCN)
- 默認基于塊級(block-level)而非行級(row-level)
- 需要表創建時指定 ROWDEPENDENCIES 才能實現行級精度
???
主要區別對比:
特性 Version Query Pseudocolumns ORA_ROWSCN 粒度 行級變更歷史 行/塊級最后修改SCN 數據源 UNDO數據 數據塊頭信息 時間范圍 需要指定時間/SCN范圍 總是顯示當前行最后的SCN 精度 精確到每次變更 精確到每次變更 使用場景 審計、數據恢復、歷史分析 樂觀鎖、變更檢測 DDL影響 表結構變更會導致ORA-01466 不受表結構變更影響 兩者結合使用,先用ORA_ROWSCN定位可疑行,再用Version Query分析詳細變更歷史
???
???
案例一:創建表,針對多次被修改的某行數據通過版本查詢偽列查看更改時間,以及修改前后的數據
(1)查看liu_jybq_org_medical表結構,創建liu_jybq_org_medical_VQ新表
SQL> create table liu_jybq_org_medical_VQ (id VARCHAR2(32) not null,region_id VARCHAR2(32) not null,hospital_short_name VARCHAR2(600),hospital_full_name VARCHAR2(600),hospital_type_code VARCHAR2(64) not null,hospital_type_name VARCHAR2(200),hospital_name_py VARCHAR2(800),hospital_class_code VARCHAR2(64),hospital_class_name VARCHAR2(200),hospital_grade_code VARCHAR2(64),hospital_grade_name VARCHAR2(120),hospital_fixed_flag VARCHAR2(4),special_subject VARCHAR2(2000),hospital_property VARCHAR2(4),linkman_person VARCHAR2(600),linkman_way VARCHAR2(200),hospital_addr VARCHAR2(2000),bank_name VARCHAR2(200),bank_account VARCHAR2(200),organization_code VARCHAR2(18),hospital_location VARCHAR2(32),hospital_profile VARCHAR2(2000),hospital_pictures VARCHAR2(100),remark CLOB,del_flag VARCHAR2(4) not null,create_user_id VARCHAR2(32) not null,create_date TIMESTAMP(6) not null,update_user_id VARCHAR2(32) not null,update_date TIMESTAMP(6) not null,region_code VARCHAR2(10),hospital_code VARCHAR2(50),is_high_risk VARCHAR2(10),high_risk_reason VARCHAR2(1000) ); -- 不指定參數默認塊級別,相關參數為NOROWDEPENDENCIESSQL> insert into liu_jybq_org_medical_VQ select * from liu_jybq_org_medical; SQL> commit;
???
(2)查看特定數據的ORA_ROWSCN偽列,并轉換為時間戳時
SQL> SELECT t1.id,t1.region_id,t1.remark,t1.hospital_name_py,t1.hospital_type_name,to_char(scn_to_timestamp(ORA_ROWSCN), 'YYYY-MM-DD HH24:MI:SS'),dbms_rowid.rowid_relative_fno(rowid) datafile_ID,dbms_rowid.rowid_block_number(rowid) block_number FROM liu_jybq_org_medical_VQ t1 where id='00345C56B7514B3EA0F996DCFF8A0824';
對特定數據進行多次更新后再查看ORA_ROWSCN偽列,并轉換為時間戳時SQL> update liu_jybq_org_medical_VQ set hospital_type_name='社區醫療站' where id='00345C56B7514B3EA0F996DCFF8A0824'; SQL> COMMIT;SQL> update liu_jybq_org_medical_VQ set hospital_type_name='鄉鎮衛生所' where id='00345C56B7514B3EA0F996DCFF8A0824'; SQL> COMMIT;SQL> SELECT t1.id,t1.region_id,t1.remark,t1.hospital_name_py,t1.hospital_type_name,to_char(scn_to_timestamp(ORA_ROWSCN), 'YYYY-MM-DD HH24:MI:SS'),dbms_rowid.rowid_relative_fno(rowid) datafile_ID,dbms_rowid.rowid_block_number(rowid) block_number FROM liu_jybq_org_medical_VQ t1 where id='00345C56B7514B3EA0F996DCFF8A0824';
可以看到了相關行數據的更新時間,但對于ORA_ROWSCN偽列而言只能看到行數據被修改的最后時間,并不能看到其他時間被修改的具體情況,那么這時候就需要使用到了版本查詢偽列
???(2)使用版本查詢偽列,查看行數據多次執行的情況,以及修改前后的數據
基于時間范圍的查詢:SQL> SELECT versions_starttime AS change_time, --- 版本開始時間/SCN versions_endtime AS end_time, --- 版本結束時間/SCNversions_xid AS transaction_id, --- 創建該版本的事務IDversions_operation AS operation, --- 操作類型(I/U/D)hospital_type_name AS new_hospital_type_name --- 該行版本中的當前值 FROM liu_jybq_org_medical_VQ VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('2025-08-14 18:18:00', 'YYYY-MM-DD HH24:MI:SS') AND TO_TIMESTAMP('2025-08-14 18:20:00', 'YYYY-MM-DD HH24:MI:SS') where id='00345C56B7514B3EA0F996DCFF8A0824' ORDER BY versions_starttime desc nulls last;
???
基于SCN的查詢:
SQL> SELECT versions_starttime AS change_time, --- 版本開始時間/SCN versions_endtime AS end_time, --- 版本結束時間/SCNversions_xid AS transaction_id, --- 創建該版本的事務IDversions_operation AS operation, --- 操作類型(I/U/D)hospital_type_name AS new_hospital_type_name --- 該行版本中的當前值 FROM liu_jybq_org_medical_VQ VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE where id='00345C56B7514B3EA0F996DCFF8A0824' ORDER BY versions_starttime desc nulls last;
通過閃回版本查詢語句配合版本查詢偽列可以查出特定hospital_type_name行字段的數據更新情況,從“幸福社區站 —> 社區醫療站 —> 鄉鎮衛生所”,使用降序將最新數據情況放在最前面
???
???
案例二:在現存表上使用版本查詢偽列,針對多次被修改的某行數據通過版本查詢偽列查看更改時間,以及修改前后的數據
(1)查看liu_jybq_org_medical業務表特定數據的ORA_ROWSCN偽列,并轉換為時間戳時
SQL> SELECT t1.id,t1.region_id,t1.remark,t1.hospital_name_py,t1.hospital_type_name,to_char(scn_to_timestamp(ORA_ROWSCN), 'YYYY-MM-DD HH24:MI:SS'),dbms_rowid.rowid_relative_fno(rowid) datafile_ID,dbms_rowid.rowid_block_number(rowid) block_number FROM liu_jybq_org_medical t1 where id='004138D1DD804D0EA8F6929E15A4480D';
liu_jybq_org_medical是一個創建很久的業務表,并且有段時間沒有更新數據了。如果查看特定數據的ORA_ROWSCN偽列,并轉換為時間戳時,會拋出如下錯誤。這是因為Oracle僅會在有限時間內保留SCN(系統變更號)與其生成時間戳的映射關系,SCN(系統變更號)與其生成時間戳的映射保留時間取的是undo數據覆蓋時間和閃回歸檔保留期的最大值,不管取值如何,scn_to_timestamp函數定義了強制最低保留時間限制,SCN 與時間戳的關聯信息最少會保留 120 小時(5 天)
對特定數據進行多次更新后再查看ORA_ROWSCN偽列,并轉換為時間戳時SQL> update liu_jybq_org_medical set hospital_type_name='社區醫療站' where id='004138D1DD804D0EA8F6929E15A4480D'; SQL> COMMIT;SQL> update liu_jybq_org_medical set hospital_type_name='鄉鎮衛生所' where id='004138D1DD804D0EA8F6929E15A4480D'; SQL> COMMIT;SQL> SELECT t1.id,t1.region_id,t1.remark,t1.hospital_name_py,t1.hospital_type_name,to_char(scn_to_timestamp(ORA_ROWSCN), 'YYYY-MM-DD HH24:MI:SS'),dbms_rowid.rowid_relative_fno(rowid) datafile_ID,dbms_rowid.rowid_block_number(rowid) block_number FROM liu_jybq_org_medical t1 where id='004138D1DD804D0EA8F6929E15A4480D';
可以看到了相關行數據的更新時間,但對于ORA_ROWSCN偽列而言只能看到行數據被修改的最后時間,并不能看到其他時間被修改的具體情況,那么這時候就需要使用到了版本查詢偽列
???(2)使用版本查詢偽列,查看行數據多次執行的情況,以及修改前后的數據
基于時間范圍的查詢:SQL> SELECT versions_starttime AS change_time, --- 版本開始時間/SCN versions_endtime AS end_time, --- 版本結束時間/SCNversions_xid AS transaction_id, --- 創建該版本的事務IDversions_operation AS operation, --- 操作類型(I/U/D)hospital_type_name AS new_hospital_type_name --- 該行版本中的當前值 FROM liu_jybq_org_medical VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('2025-08-14 18:20:00', 'YYYY-MM-DD HH24:MI:SS') AND TO_TIMESTAMP('2025-08-14 18:25:00', 'YYYY-MM-DD HH24:MI:SS') where id='004138D1DD804D0EA8F6929E15A4480D' ORDER BY versions_starttime desc nulls last;
???
基于SCN的查詢:
SQL> SELECT versions_starttime AS change_time, --- 版本開始時間/SCN versions_endtime AS end_time, --- 版本結束時間/SCNversions_xid AS transaction_id, --- 創建該版本的事務IDversions_operation AS operation, --- 操作類型(I/U/D)hospital_type_name AS new_hospital_type_name --- 該行版本中的當前值 FROM liu_jybq_org_medical VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE where id='004138D1DD804D0EA8F6929E15A4480D' ORDER BY versions_starttime desc nulls last;
通過閃回版本查詢語句配合版本查詢偽列可以查出特定hospital_type_name行字段的數據更新情況,從“其他 —> 社區醫療站 —> 鄉鎮衛生所”,使用降序將最新數據情況放在最前面
總結與最佳實踐
????閃回版本查詢為Oracle用戶提供了強大的數據追溯能力,通過 VERSIONS_* 偽列可精準還原行數據變更軌跡,如同數據庫的"時間機器"?。使用時需注意:
1?? 時效性:盡早查詢,避免UNDO數據因空間壓力或超出UNDO_RETENTION
期限被覆蓋
2?? 擴展性:對于長期審計需求,建議結合閃回數據歸檔(Flashback Data Archive)
實現歷史數據永久保存