DB2 for i でフラッシュバック・クエリ!? (V5R4)

ジャーナルには更新されたレコード(行)の前後イメージがそのまま入っています。
また、ILE RPG では、EXTNAME キーワードを使用してレコード様式をそのままデータ・ストラクチャーとして定義することができます。

レコードがそのまま入っているのであれば、それをこのレコード様式でオーバーライドしてしまえば、フィールド単位で内容を確認できるはずですね。
↑の 2つを組み合わせれば、それができるということになりますが … 実際、できるんです。

先に実行イメージからの画面コピーをお見せしてしまいますが、↓のようにジャーナルのデータから更新行の各カラムをそれぞれ取り出して参照することができるわけです。

情報のおおもとはジャーナルなので、日付を指定して参照することができますね。

つまり、ジャーナルさえとっておいてあれば日付を指定して、その時点でのテーブルの状態をカラム単位で見ることができるわけです。


ジャーナル内の情報とレコード定義のマッピングを行う

↓が、↑で概略を述べたことを実際に行うプログラム例です。

     H DFTACTGRP(*NO)                                                                               
      *                                                                                             
     D JORDJE        E DS                  EXTNAME(QJORDJE)                                         
      *                                                                                             
     D FILE          E DS                  EXTNAME(QCUSTCDT)                                        
     D                                     BASED(p)                                                 
      *                                                                                             
     D p               S               *                                                            
      /free                                                                                         
                                                                                                    
          exec sql DECLARE C1 CURSOR FOR                                                            
                   SELECT *                                                                         
                     FROM QJORDJE                                                                   
                 ORDER BY JOSEQN ;                                                                  
                                                                                                    
          exec sql OPEN C1 ;                                                                        
                                                                                                    
          dou (%subst(SQLSTATE:1:2) <> '00') ;                                                      
             exec sql FETCH C1 INTO :JORDJE ;                                                       
             if %subst(SQLSTATE:1:2) = '02' ;                                                       
                leave ;                                                                             
             endif ;                                                                                
             if JOCODE = 'R' ;                                                                      
                p = %addr(joesd) ;                                                                  
                dsply '====== '    ;                                                                
                // Logging Information                                                              
                dsply JODATE ;                                                                      
                dsply JOTIME ;                                                                      
                dsply JOENTT ;                                                                      
                dsply JOJOB  ;                                                                      
                dsply JOUSER ;                                                                      
                dsply JOPGM  ;                                                                      
                dsply JOOBJ  ;                                                                      
                dsply JOLIB  ;                                                                      
                dsply JOCTRR ;                                                                      
                dsply JOCCID ;                                                                      
                // Table Row Information                                                            
                dsply CUSNUM ;                                                                      
                dsply LSTNAM ;                                                                      
                dsply INIT   ;                                                                      
                dsply STREET ;                                                                      
                dsply CITY   ;                                                                      
                dsply STATE  ;                                                                      
                dsply ZIPCOD ;                                                                      
                dsply CDTLMT ;                                                                      
                dsply CHGCOD ;                                                                      
                dsply BALDUE ;                                                                      
                dsply CDTDUE ;                                                                      
             endif ;                                                                                
          enddo ;                                                                                   
                                                                                                    
          exec sql CLOSE C1 ;                                                                       
          *inlr = *on ;                                                                             
          return ;                                                                                  
                                                                                                    
      /end-free              

テーブル毎にプログラムが必要にはなりますが、↑のようにシンプルなものなので必要になった時に応じてカンタンに作ることができます。

ジャーナルさえとってあればよく、あらかじてトラッキングしたいものについてプログラムを作っておくとか専用の設定をかけておく、といったことが必要なわけではありません。見たい時にプログラムを書く、でもけっこう間に合ってしまうようにも思います。

また、↑のプログラムを使って、テーブルの更新レコードのそれぞれについて、やはりジャーナルの中から日付や更新ジョブ/ユーザーなどを追加したトラッキング用のテーブルを、定期的に作成しておく、ということも可能ですね。

日付を指定して、該当期間のジャーナルを抽出する

ジャーナルを出力したり、その期間を指定したりできるように CL で↑の呼び出しプログラムを作ってみました。

             PGM        PARM(&FDATE &FTIME &TDATE &TTIME)                       
             DCL        VAR(&FDATE) TYPE(*CHAR) LEN(8)                          
             DCL        VAR(&TDATE) TYPE(*CHAR) LEN(8)                          
             DCL        VAR(&FTIME) TYPE(*CHAR) LEN(6)                          
             DCL        VAR(&TTIME) TYPE(*CHAR) LEN(6)                          
             /* エラーハンドリング用変数*/                                                  
             DCL        VAR(&MSGID) TYPE(*CHAR) LEN(7)                          
             DCL        VAR(&MSGF) TYPE(*CHAR) LEN(10)                          
             DCL        VAR(&MSGFLIB) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&MSGDTA) TYPE(*CHAR) LEN(512)                       
             DCL        VAR(&ABENDING) TYPE(*LGL)                               
             DCL        VAR(&MSGKEY) TYPE(*CHAR) LEN(4)                         
             DCL        VAR(&MSGTYPE) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&RTNTYPE) TYPE(*CHAR) LEN(2)                        
             DCL        VAR(&PGMNAME) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&SENDER) TYPE(*CHAR) LEN(80)                        
                                                                                
             /* エラーキャッチ */                                                      
             MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))                 
                                                                                
             SNDPGMMSG  MSG(' ') TOPGMQ(*SAME) MSGTYPE(*INFO) +                 
                          KEYVAR(&MSGKEY)                                       
             RCVMSG     PGMQ(*SAME) MSGTYPE(*INFO) RMV(*YES) +                  
                          SENDER(&SENDER)                                       
             CHGVAR     VAR(&PGMNAME) VALUE(%SST(&SENDER 56 10))                
                                                                                
             /* プログラム本文 */                                                      
             IF         COND((&FDATE = '        ') & (&TDATE = +                
                          '        ')) THEN(GOTO CMDLBL(EXIT))                  
             IF         COND(&FDATE = '        ') THEN(CHGVAR +                 
                          VAR(&FDATE) VALUE(&TDATE))                            
             IF         COND(&TDATE = '        ') THEN(CHGVAR +                 
                          VAR(&TDATE) VALUE(&FDATE))                            
             IF         COND((&FTIME = '      ') & (&TTIME = '      +           
                          ')) THEN(DO)                                          
             CHGVAR     VAR(&FTIME) VALUE('000000')                             
             CHGVAR     VAR(&TTIME) VALUE('235959')                             
             ENDDO                                                              
             IF         COND(&FTIME = '      ') THEN(DO)                        
             CHGVAR     VAR(&FTIME) VALUE(&TTIME)                               
             CHGVAR     VAR(&TTIME) VALUE('235959')                             
             ENDDO                                                              
             DSPJRN     JRN(QIWSTEST/QSQJRN) +                                  
                          FILE((QIWSTEST/QCUSTCDT)) +                           
                          RCVRNG(*CURCHAIN) FROMTIME(&FDATE &FTIME) +           
                          TOTIME(&TDATE &TTIME) ENTTYP(*RCD) +                  
                          OUTPUT(*OUTFILE) OUTFILFMT(*TYPE1) +                  
                          OUTFILE(QTEMP/QJORDJE)                                
         /*  MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(EXIT))  */              
             CALL       PGM(APITEST/DSPFILJRNR)                                 
                                                                                
             /* 終了処理 */                                                         
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('プログラム' || &PGMNAME |< +                       
                          'は正常に終了した。') MSGTYPE(*COMP)                           
             RETURN                                                             
                                                                                
 ERROR:      IF         COND(&ABENDING) THEN(RETURN)                            
             CHGVAR     VAR(&ABENDING) VALUE('1')                               
             RCVMSG     MSGTYPE(*LAST) MSGDTA(&MSGDTA) MSGID(&MSGID) +          
                          RTNTYPE(&RTNTYPE) MSGF(&MSGF) +                       
                          MSGFLIB(&MSGFLIB)                                     
             IF         COND((&RTNTYPE = '15') *OR (&RTNTYPE = +                
                          '17')) THEN(DO)                                       
             SNDPGMMSG  MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +                    
                          MSGDTA(&MSGDTA) MSGTYPE(*DIAG)                        
             ENDDO                                                              
 ESCAPE:     SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('プログラム' || &PGMNAME |< +                       
                          'は異常終了した。') MSGTYPE(*ESCAPE)                          
                                                                                
 EXIT:       ENDPGM

こんなかんじで呼び出します。

[Top Pageに戻る]

Ads by TOK2