Bo's Oracle Station

【博客文章2022】同时包含有效时间维度和事务历史时间维度的bi-temporal查询

2022-3-27 22:08| 发布者: admin| 查看: 95| 评论: 0|原作者: Bo Tang

摘要: 混合有效性时间维度和事务时间维度的bi-temporal查询
【博客文章2022】同时包含有效时间维度和事务历史时间维度的bi-temporal查询

Author: Bo Tang

1. Oracle表中时间字段的两种维度:

     Oracle表中时间字段存在两种维度
    a. 有效时间维度对你的业务应用而言,日期字段本身的有效性或有意义性。
    b. 事务历史时间维度日期字段上或其他字段上曾经提交过的历史值,这些值应该存放在闪回数据归档里。
    事务历史时间维度可以包含如下一些扩展属性插入该日期数据的日期、做这个改动的人和ip 地址等等。
    在flashback as of查询和flashback versions查询默认都只是显示事务历史时间维度,并且不包含任何扩展属性。利用以下实验回顾一下:
    1)准备环境:

     2)查看当前会话的上下文

SQL>  select sys_context('userenv','SESSION_USER') from dual;
SYS_CONTEXT('USERENV','SESSION_USER')
--------------------------------------------------------------------------------
SH
SQL> select sys_context('userenv','ip_address')  from dual;
SYS_CONTEXT('USERENV','IP_ADDRESS')
--------------------------------------------------------------------------------
192.168.1.85
SQL> select sys_context('userenv','current_user')  from dual;

SYS_CONTEXT('USERENV','CURRENT_USER')
--------------------------------------------------------------------------------
SH
SQL> select sys_context('userenv','OS_USER') from dual;

SYS_CONTEXT('USERENV','OS_USER')
--------------------------------------------------------------------------------
oracle
SQL> select sys_context('userenv','authentication_method')  from dual;

SYS_CONTEXT('USERENV','AUTHENTICATION_METHOD')
--------------------------------------------------------------------------------
PASSWORD

 
     3)查看事务历史时间维度扩展属性
    首先,由sys进行授权dbms_flashback_archive的执行:

SQL> conn / as sysdba
Connected.
SQL> grant execute on dbms_flashback_archive to sh;
Grant succeeded.
   
    sh用户查询不到事务历史时间维度扩展属性

SQL>select  versions_xid, versions_startscn, versions_operation , a ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','SESSION_USER')  su ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','ip_address')  ip ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','current_user')   cu ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','os_user')  ou,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','authentication_method')  am
              from t_timedim
  versions between scn minvalue and maxvalue;
-----------------------------------------------------------------------------------------------------------------------------------------------
01001A0009090000    4057367    I    5                   
06001600030B0000    4057362    I    4                   
02000F0062090000    4057356    I    3                   
0A000A00ED0A0000    4057352    I    2                   
0300100037090000    4057347    I    1                   
 
    为了能够查看事务历史时间维度扩展属性,首先 t_timedim这个表绑定闪回数据归档:

 
    然后只需要以sys身份执行一次以下的SET_CONTEXT_LEVEL过程:

SQL>conn / as sysdba
Connected.
SQL>begin
   DBMS_FLASHBACK_ARCHIVE.SET_CONTEXT_LEVEL('TYPICAL');
  end;

/
PL/SQL procedure successfully completed.
 

    附:如果要取消SET_CONTEXT_LEVEL,则执行下面代码:

SQL>conn / as sysdba
Connected.
SQL>begin
   DBMS_FLASHBACK_ARCHIVE.
SET_CONTEXT_LEVEL('NONE');
  end;

 /
PL/SQL procedure successfully completed.
 
    sh用户继续在t_timedim表上产生新的事务:

 
     sh用户查询到了事务历史时间维度扩展属性
    方法1:

SQL>select  versions_xid, versions_startscn, versions_operation , a ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','SESSION_USER')  su ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','ip_address')  ip ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','current_user')   cu ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','os_user')  ou,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(VERSIONS_XID, 'USERENV','authentication_method')  am
              from t_timedim
  versions between scn minvalue and maxvalue;
-----------------------------------------------------------------------------------------------------------------------------------------------
08000200040A0000    4074822    U    100    SH    192.168.1.85    SYS    oracle   
05000A0078090000    4074832    U    200   
SH    192.168.1.85    SYS    oracle                
03000D004A090000    4074838    U    300   
SH    192.168.1.85    SYS    oracle                  
0A001E00060B0000    4074856    U    400   
SH    192.168.1.85    SYS    oracle                 
06001D000D0B0000   4074862     U   500   
SH    192.168.1.85    SYS    oracle                  
    
    方法2,直接查看flashback data archive:

    
SQL>   select    a, startscn , DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(XID, 'USERENV','SESSION_USER')  su ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(XID, 'USERENV','ip_address')  ip ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(XID, 'USERENV','current_user')   cu ,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(XID, 'USERENV','os_user')  ou,
            DBMS_FLASHBACK_ARCHIVE.GET_SYS_CONTEXT(XID, 'USERENV','authentication_method')  am 
            from sys_fba_hist_116155 where startscn is not null; 
-----------------------------------------------------------------------------------------------------------------------------------------------
100   4074822     SH    192.168.1.85    SYS    oracle  
200  
4074832     SH    192.168.1.85    SYS    oracle  
300  
4074838     SH    192.168.1.85    SYS    oracle
400  
4074856     SH    192.168.1.85    SYS    oracle
500  
4074862     SH    192.168.1.85    SYS    oracle
          
2. 在有效时间维度上利用period for来实现自动添加where条件(显式方法):

SQL>CREATE TABLE t_emp
 ( empid number, salary number, deptid number,
  last_name VARCHAR2(20), first_name varchar2(20),
  hire_time_start DATE, hire_time_end DATE,
  PERIOD FOR hire_time (hire_time_start,hire_time_end));
Table created.
 
3. 在有效时间维度上利用period for来实现自动添加where条件(隐式方法):
   
    与上面的显式方法相比较,除了不用指定hire_time_start和hire_time_end以外,其他都一样:

SQL> CREATE TABLE t_emp2
 ( empid number, salary number, deptid number,
 last_name VARCHAR2(20), first_name varchar2(20),

 PERIOD FOR hire_time);
Table created.
 
    下面实验都在t_emp2表上操作,t_emp表的实验方法和结果和t_emp2表的完全一样。
    1)准备环境:

 
    附:如果插入t_emp表,也可以只要像下面这样操作,即插入时不必写字段列表:

SQL>  insert into t_emp values     (100,10000.00, 90, 'King','Steven', to_date('2010-01-01','YYYY-MM-DD'), to_date('2022-01-01','YYYY-MM-DD'));
1 row created.
SQL> commit;
Commit complete.
SQL> insert into t_emp values     (200,10000.00, 90, 'Whalen','Steven', to_date('2011-01-01','YYYY-MM-DD'), to_date('2011-07-01','YYYY-MM-DD'));
1 row created.
SQL> commit;
Commit complete.
SQL> insert into t_emp values     (300,10000.00, 90, 'Kochhar','Neena', to_date('2012-01-01','YYYY-MM-DD'), to_date('2022-01-01','YYYY-MM-DD'));
1 row created.
SQL> commit;
Commit complete.

  
    表中的数据如图所示:


    2)明确写清楚period for的查询:
    希望显示2010-01-01存在的员工(有效时间里的数据),相当于select  * from t_emp2 where hire_time_start <=  to_date('2010-01-01','YYYY-MM-DD') and hire_time_end to_date('2010-01-01','YYYY-MM-DD'):

    图中黄色部分为结果集,代表period for hire_time这个时间点落在表上数据的hire_time_start和hire_time_end之间。

SQL> select  * from t_emp2 as of period for hire_time to_date('2010-01-01','YYYY-MM-DD');
---------------------------------------------------------------------------------------
100    10000    90    King    Steven    01-JAN-10    01-JAN-22
 
     希望显示2011-01-01存在员工(有效时间里的数据),相当于select  * from t_emp2 where hire_time_start <=  to_date('2011-01-01','YYYY-MM-DD') and hire_time_end >  to_date('2011-01-01','YYYY-MM-DD')

    图中黄色部分为结果集,代表period for hire_time这个时间点落在表上数据的hire_time_start和hire_time_end之间。


SQL> select  * from t_emp2 as of period for hire_time  to_date('2011-01-01','YYYY-MM-DD');
--------------------------------------------------------------------------------------
100    10000    90    King    Steven    01-JAN-10    01-JAN-22
200    10000    90    Whalen    Steven    01-JAN-11    01-JUL-11

     希望显示2012-01-01存在员工(有效时间里的数据),相当于select  * from t_emp2 where hire_time_start <=  to_date('2012-01-01','YYYY-MM-DD') and hire_time_end to_date('2012-01-01','YYYY-MM-DD'):

    图中黄色部分为结果集,代表period for hire_time这个时间点落在表上数据的hire_time_start和hire_time_end之间。

SQL> select  * from t_emp2 as of period for hire_time to_date('2012-01-01','YYYY-MM-DD');
----------------------------------------------------------------------------------------
100    10000    90    King    Steven    01-JAN-10    01-JAN-22
300    10000    90    Kochhar    Neena    01-JAN-12    01-JAN-22
 
    希望显示2021-12-31存在员工(有效时间里的数据)相当于select  * from t_emp2 where hire_time_start <=  to_date('2021-12-31','YYYY-MM-DD') and hire_time_end to_date('2021-12-31','YYYY-MM-DD')

     图同上


SQL> select  * from t_emp2 as of period for hire_time to_date('2021-12-31','YYYY-MM-DD');
------------------------------------------------------------------------------------------
100    10000    90    King    Steven    01-JAN-10    01-JAN-22
300    10000    90    Kochhar    Neena    01-JAN-12    01-JAN-22
 
    希望显示2022-01-01存在员工(有效时间里的数据)相当于select  * from t_emp2 where hire_time_start <=  to_date('2022-01-01','YYYY-MM-DD') and hire_time_end >  to_date('2022-01-01','YYYY-MM-DD') 


    图中黄色部分为结果集,代表period for hire_time这个时间点落在表上数据的hire_time_start和hire_time_end之间。

SQL> select  * from t_emp2 as of period for hire_time to_date('2022-01-01','YYYY-MM-DD');
------------------------------------------------------------------------------------------
没有数据
    
    注意之所以是“<= 和 >”这样的组合是因为:Oracle强制要求endtime必须大于starttime,不能大于等于。
    希望显示从2011-07-01到2022-01-01期间存在员工(有效时间里的数据)相当于select  * from t_emp2 where (hire_time_start <=  to_date('2022-01-01','YYYY-MM-DD') and  hire_time_end > to_date('2011-07-01','YYYY-MM-DD')) ;
    代表period for hire_time between and之间的时间段落在表上数据的hire_time_start和hire_time_end之间。

SQL> select  * from t_emp2  versions period for hire_time between to_date('2011-07-01','YYYY-MM-DD') and to_date('2022-01-01','YYYY-MM-DD');
--------------------------------------------------------------------------------------
100    10000    90    King    Steven
300    10000    90    Kochhar    Neena

    3)利用dbms_flashback_archive自动添加where条件的查询:
    希望显示2010-01-01存在的员工(有效时间里的数据),相当于select  * from t_emp2 where hire_time_start <=  to_date('2010-01-01','YYYY-MM-DD') and hire_time_end to_date('2010-01-01','YYYY-MM-DD'):

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('ASOF', (to_date('2010-01-01','YYYY-MM-DD')));
 end;
 /
PL/SQL procedure successfully completed.
SQL> select  * from t_emp2;
-------------------------------------------------
100    19999    90    King    Steven    01-JAN-10    01-JAN-22

 
    希望显示2011-01-01存在员工(有效时间里的数据),相当于select  * from t_emp2 where hire_time_start <=  to_date('2011-01-01','YYYY-MM-DD') and hire_time_end >  to_date('2011-01-01','YYYY-MM-DD')

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('ASOF', (to_date('2011-01-01','YYYY-MM-DD')));
 end;
 /
PL/SQL procedure successfully completed.
SQL> select  * from t_emp2;
-------------------------------------------------
100    10000    90    King    Steven    01-JAN-10    01-JAN-22
200    10000    90    Whalen    Steven    01-JAN-11    01-JUL-11
   
    希望显示2012-01-01存在员工(有效时间里的数据),相当于select  * from t_emp2 where hire_time_start <=  to_date('2012-01-01','YYYY-MM-DD') and hire_time_end to_date('2012-01-01','YYYY-MM-DD'):

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('ASOF', (to_date('2012-01-01','YYYY-MM-DD')));
 end;
 /
PL/SQL procedure successfully completed.
SQL> select  * from t_emp2;
-------------------------------------------------
100    10000    90    King    Steven
300    10000    90    Kochhar    Neena

 
    希望显示2021-12-31存在员工(有效时间里的数据)相当于select  * from t_emp2 where hire_time_start <=  to_date('2021-12-31','YYYY-MM-DD') and hire_time_end to_date('2021-12-31','YYYY-MM-DD')

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('ASOF', (to_date('2021-12-31','YYYY-MM-DD')));
 end;
 /

PL/SQL procedure successfully completed.

SQL> select  * from t_emp2;
-------------------------------------------------
100    10000    90    King    Steven
300    10000    90    Kochhar    Neena

    希望显示2022-01-01存在员工(有效时间里的数据)相当于select  * from t_emp2 where hire_time_start <=  to_date('2022-01-01','YYYY-MM-DD') and hire_time_end >  to_date('2022-01-01','YYYY-MM-DD')

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('ASOF', (to_date('2022-01-01','YYYY-MM-DD')));
 end;
 /

PL/SQL procedure successfully completed.
SQL> select  * from t_emp2;
-------------------------------------------------
没有数据

    希望显示但今存在员工(有效时间里的数据)相当于select  * from t_emp2 where hire_time_start <=  sysdate and hire_time_end >  sysdate

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('CURRENT');
 end;
 /

PL/SQL procedure successfully completed.

SQL> select  * from t_emp2;
-------------------------------------------------
没有数据

    希望取消自动添加的where条件

SQL> begin
 dbms_flashback_archive.enable_at_valid_time('ALL');
 end;
 /
PL/SQL procedure successfully completed.
SQL> select  * from t_emp2;
-------------------------------------------------
100    10000    90    King    Steven
200    10000    90    Whalen    Steven
300    10000    90    Kochhar    Neena

4. 同时包含有效时间维度事务历史时间维度的bi-temporal查询:

    1)对t_emp2表做一下更新,并提交:
      
    
    2)明确写清楚period for的bi-temporal 查询:
   这样既显示了日期字段本身的有效性或有意义性(希望显示2010-01-01存在的员工),也显示了日期字段上或其他字段上曾经提交过的历史值(10000):
    
SQL> select  empid,salary,deptid, last_name, first_name,hire_time_start,hire_time_end from t_emp2 as of period for hire_time to_date('2010-01-01','YYYY-MM-DD') as of timestamp sysdate-2/1440;
------------------------------------------------------------------------------------------

100    10000    90    King    Steven    01-JAN-10    01-JAN-22
 
    3)利用dbms_flashback_archive自动添加where条件的bi-temporal 查询:

 
    这样看来,对于bi-temporal 查询,Oracle应该先做了闪回查询:见到了 01-JAN-10的日期(而不是当前的02-JAN-10),然后再使用period for做过滤。

路过

雷人

握手

鲜花

鸡蛋

QQ|手机版|Bo's Oracle Station   

GMT+8, 2022-3-31 23:08 , Processed in 0.040541 second(s), 21 queries .

返回顶部