Tuesday, January 28, 2025

A Matter of Time - Part 1 - Intro

It was only a matter of time before I posted an article about my TDF_LAP() function - which is literally a "matter of time". I created this useful function to measure the timings of routines and SQL statements in SQR batch processes. It's actually a set of timing entries that can be nested - levels 0 thru 20. In general, a simple BEG and END call will calculate the elapsed time in between and print the results on a log.

Above: LEGO Guy returns - he's going to replicate the TDF_LAP function in Asssembler. Stay tuned...

Flashback: LEGO Guy loves Assembler

Sample Log Output - AP Vendor/Sourcing Extract

Process_Main uses timing set number 1 and contains all the other routines which use the stand-alone level of zero. The nested levels end and appear first followed by the high level Process_Main routine which displays the total BEG/END information.

 

 

 

TDF_LAP() Function

Below are the usage notes & parameter descriptions for the TDF_LAP() function. The LOG Y/N option is useful if a routine or SQL statement is executed multiple times - say 1000 or more. The calculated lapsed time (#O_sec) can then be aggregated & printed after all instances have executed instead of printing 1000+ log entries.

!**********************************************************************
!*       LAPSE Between Begin/End Times                                *
!**********************************************************************
!*                                                                    *
!*        INPUT: $I_oper    - Operation Type (BEG/END)                *
!*               #I_idx     - Lapse Index Set (0 thru 20)             *
!*               $I_log     - Display Log Output? (Y/N)               *
!*               $I_msg     - Message Text                            *
!*       OUTPUT: #O_sec     - Lapsed in seconds                       *
!*               $O_hms     - Lapsed in HH:MI:SS Format               *
!*                                                                    *
!**********************************************************************
!*                                                                    *
!*      EXAMPLE: do TDF_LAP('BEG', 0, 'N','', #O_sec, $O_hms)         *
!*               ...                                                  *
!*               do TDF_LAP('END', 0, 'Y', 'Lapsed', #O_sec, $O_hms)  *
!*                                                                    *
!*      RESULTS: Displays Duration between BEG/END times.             *
!*               Returns Duration in both seconds and HH:MI:SS.       *
!*               Resets BEG time to END time for next lapse message.  *
!*               Index Set 0 used for timings (0 thru 20 available) - *
!*               use different sets for nested timings (otherwise 0). *
!*                                                                    *
!**********************************************************************

Sample - Nested Timings

The routine below is part of a much larger process - a custom Voucher Edit/Build module. The routine NSS_Edit_AP_Invoice_Dup will use timing levels 7, 8 and 9 to track each nested component of the routine.

!**********************************************************************
!*       NSS Edit AP Invoice Duplicate                                *
!**********************************************************************

begin-procedure NSS_Edit_AP_Invoice_Dup

do TDF_LAP('BEG', 7, 'N', '', #O_sec, $O_hms)

let $DUP_VNDR               = ' and a.vendor_setid  = ~<SETID>~ '   ||
                              ' and a.vendor_id     = u.vendor_id '

let $DUP_VNDR               = replace($DUP_VNDR, '<SETID>', $NSS_setid_vendor)
let $DUP_VNDR               = replace($DUP_VNDR, '~', chr(39))

create-array name=DUPmtx size=5 field=DUPsql:char

let DUPmtx.DUPsql (0)       = ' and a.business_unit = u.business_unit '
let DUPmtx.DUPsql (1)       = $DUP_vndr
let DUPmtx.DUPsql (2)       = ' and a.invoice_id    = u.invoice_id '
let DUPmtx.DUPsql (3)       = ' and a.invoice_dt    = u.invoice_dt '
let DUPmtx.DUPsql (4)       = ' and a.gross_amt     = u.invoice_amount '

!   Initialize Defaults at Origin/BU Levels

let #DUPdef                 = #OPT_dup_inv_comb_code

!   Update Staging Detail with Specified/Default Combination Options

let $SQL_proc               = 'NSS_Edit_AP_Invoice_Dup (Options)'
let $SQL_action             = 'Update'
let $SQL_table              = 'XX_API_STG_DTL'

do TDF_LAP('BEG', 8, 'N', '', #O_sec, $O_hms)

begin-sql On-Error=Error-Routine

update ps_xx_api_stg_dtl      u
   set (u.dup_invoice_ind,
        u.dup_inv_comb_code) =
       (select a.dup_invoice_ind,
               decode(a.dup_invoice_ind,
               'D', #DUPdef,
                 decode(a.dup_invoice_action,
                   'K',a.dup_inv_comb_code,0))
          from ps_vendor_invoice   a
         where a.setid           = $NSS_setid_vendor
           and a.vendor_id       = u.vendor_id
           and a.effdt           =
               (select max(a2.effdt)
                  from ps_vendor_invoice   a2
                 where a2.setid          = a.setid
                   and a2.vendor_id      = a.vendor_id
                   and a2.effdt         <= sysdate))
 where u.business_unit      = $NSS_business_unit
   and u.xx_batch_type      = $NSS_xx_batch_type
   and u.xx_batch_id        = #NSS_xx_batch_id
   and u.xx_line_status     = 'P'
   and exists
       (select 'X'
          from ps_vendor_invoice   a
         where a.setid           = $NSS_setid_vendor
           and a.vendor_id       = u.vendor_id
           and a.effdt          <= sysdate)

end-sql

do TDF_LAP('END', 8, 'Y', $SQL_proc, #O_sec, $O_hms)

!    Cycle Thru Distinct Combinations (Dynamic SQL)

do TDF_LAP('BEG', 8, 'N', '', #O_sec, $O_hms)

begin-select

dup.dup_inv_comb_code

  let $DUPwhere             = ''
  let #DUPcode              = &dup.dup_inv_comb_code
  let #DUPmap               = &dup.dup_inv_comb_code
  while #DUPmap             > 0
     do TDF_ERRBIT(#DUPmap, #DUPidx, #DUPval)
     let $DUPsql            = DUPmtx.DUPsql (#DUPidx)
     let $DUPwhere          = $DUPwhere || $DUPsql
  end-while

  do NSS_Edit_AP_Invoice_Dup_SQL

 from ps_xx_api_stg_dtl       dup
where dup.business_unit     = $NSS_business_unit
  and dup.xx_batch_type     = $NSS_xx_batch_type
  and dup.xx_batch_id       = #NSS_xx_batch_id
  and dup.xx_line_status    = 'P'
  and dup.dup_inv_comb_code > 0
group by dup.dup_inv_comb_code

end-select

let $SQL_proc               = 'NSS_Edit_AP_Invoice_Dup [SQL Cycles]'

do TDF_LAP('END', 8, 'Y', $SQL_proc, #O_sec, $O_hms)

let $SQL_proc               = 'NSS_Edit_AP_Invoice_Dup (Total)'

do TDF_LAP('END', 7, 'Y', $SQL_proc, #O_sec, $O_hms)

end-procedure

!**********************************************************************
!*       NSS Edit AP Invoice Duplicates (per SQL Combination)         *
!**********************************************************************

begin-procedure NSS_Edit_AP_Invoice_Dup_SQL

let $SQL_proc              = 'NSS_Edit_AP_Invoice_Dup_SQL'
let $SQL_action            = 'Update'
let $SQL_table             = 'XX_API_STG_DTL'

do TDF_LAP('BEG', 9, 'N', '', #O_sec, $O_hms)

begin-sql On-Error=Error-Routine

update ps_xx_api_stg_dtl     u
   set u.xx_err_ap         = u.xx_err_ap + {ERR_AP_duplicate},
       u.xx_errors         = u.xx_errors + 1
 where u.business_unit     = $NSS_business_unit
   and u.xx_batch_type     = $NSS_xx_batch_type
   and u.xx_batch_id       = #NSS_xx_batch_id
   and u.xx_line_status    = 'P'
   and u.dup_inv_comb_code = #DUPcode
   and exists
      (select 'X'
         from ps_voucher        a
        where 1 = 1
         [$DUPwhere]
          and a.business_unit in
              (select z.setcntrlvalue
                 from ps_set_cntrl_rec   z
                where z.setid          = $NSS_setid_vendor
                  and z.recname        = 'VENDOR')
          and a.invoice_id    = u.invoice_id
          and a.entry_status <> 'X'
          and a.close_status <> 'C');

commit;

end-sql

let $SQL_proc              = $SQL_proc || ' [' || to_char(#DUPcode) || ']'

do TDF_LAP('END', 9, 'Y', $SQL_proc, #O_sec, $O_hms)

end-procedure

!**********************************************************************

Portion of LOG Output:



 

The TDF_LAP() calls are highlighted in the code above. The 'nested' timings use levels 7, 8 and 9 in this example. The entire routine took .49 micro-seconds as shown by the last line (level 7 timing). The two nested level 8 entries show .32 and .17 microseconds - level 9 is a subset of the second level 8 entry at .06 microseconds.

The TDF_LAP() function is a staple in all my SQR processes.

Coming up - COBOL and Assembler/370 versions.