【Oracle篇】伪列之Version Query:全链路追踪行数据变更的所有记录(除记录行数据的最后修改时间外,还可追溯其修改前后的内容及对应的修改时间)(第二篇,总共六篇)

💫《博主主页》:
   🔎 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:实现Top-N查询和传统分页(含通过 ROW_NUMBER()函数 的实现方式)
  • 第六篇:伪列之Hierarchical Query、COLUMN_VALUE、OBJECT_ID、OBJECT_VALUE、XMLDATA

   

特别说明💥:本篇文章部分理论性知识点均来源于版权归 Oracle 所有的官方公开文档手册,并结合了我个人的解读和案例演示。若需要调整,请联系,会尽快处理😄
   
官方文档对于版本查询伪列的介绍(Oracle 12c):
Version Query Pseudocolumns
在这里插入图片描述



   
   

一、版本查询伪列介绍:

    版本查询伪列仅在闪回版本查询(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 QueryUndo数据 / undo_retention行级undo_retention单点数据查看
Flashback Version QueryUndo数据 / undo_retention行版本级undo_retention变更历史追踪
Flashback Transaction QueryUndo+Flashback日志 / undo_retention事务级undo_retention事务分析
Flashback TableUndo数据 / 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 条件];

参数说明:startend分别代表要查询时间区间的起始点和结束点表达式。该时间区间为闭区间[包含startend时刻]

    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 PseudocolumnsORA_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)
);   -- 不指定参数默认块级别,相关参数为NOROWDEPENDENCIES

SQL> 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伪列而言只能看到行数据被修改的最后时间,并不能看到其他时间被修改的具体情况,那么这时候就需要使用到了版本查询伪列
在这里插入图片描述
   

(3)使用版本查询伪列,查看行数据多次执行的情况,以及修改前后的数据

  • 基于时间范围的查询:
SQL>
SELECT 
versions_starttime AS change_time,        --- 版本开始时间/SCN
versions_endtime AS end_time,             --- 版本结束时间/SCN
   versions_xid AS transaction_id,           --- 创建该版本的事务ID
   versions_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,             --- 版本结束时间/SCN
   versions_xid AS transaction_id,           --- 创建该版本的事务ID
   versions_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,             --- 版本结束时间/SCN
   versions_xid AS transaction_id,           --- 创建该版本的事务ID
   versions_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,             --- 版本结束时间/SCN
   versions_xid AS transaction_id,           --- 创建该版本的事务ID
   versions_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)实现历史数据永久保存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奈斯DB

打赏到账,我飘啦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值