Significant performance improvement with the use of bind variables

We had a user who needed to run an adhoc report for 24k account numbers. The structure of the account table was as follows:

COL_A NUMBER(3,0) DEFAULT ON NULL 0 NOT NULL ENABLE
COL_B NUMBER(3,0) DEFAULT ON NULL 0 NOT NULL ENABLE
COL_C NUMBER(4,0) DEFAULT ON NULL 0 NOT NULL ENABLE
About 100 other columns

The table had 60 million rows and occupied about 34 gigabytes of space.

There was an index on COL_A, COL_B, COL_C plus other trailing columns.

The list of account numbers was provided as
0020510043
1024100289
And so on

A new list of account numbers may be provided every day or multiple times a day and hence the process needed to be repeatable. The first attempt was to loop through this list and execute SQL statement such as:

  select    col_01, col_02, col_03, col_04, col_05, col_06, col_07
  from      tbowner.tbname
  where     col_a = 002 
    and     col_b = 051
    and     col_c = 0043
  ;
  select    col_01, col_02, col_03, col_04, col_05, col_06, col_07
  from      tbowner.tbname
  where     col_a = 102
    and     col_b = 410
    and     col_c = 0289
  ;

and so on 24k times. The run time for this execution was about 300 minutes.

I changed the code to use bind variables as shown below and the execution time dropped to 22 minutes.

#!/bin/ksh
PATH=$PATH:/oracle_home/12.1.0.2
export ORACLE_SID=PROD4

function run_the_sql
{
  #echo 'run_the_sql - start' `date`
  
  /oracle_home/12.1.0.2/bin/sqlplus -s / as sysdba << EOF >> report.txt
  set linesize 150
  set heading off
  set pagesize 50000; 
  set echo off; 
  set feedback off; 
  
  var    col_a_bind number;
  var    col_b_bind number;
  var    col_c_bind number;
  
  exec :col_a_bind :=${col_a_os};
  exec :col_b_bind :=${col_b_os};
  exec :col_c_bind :=${col_c_os};
  
  select    col_01, col_02, col_03, col_04, col_05, col_06, col_07
  from      tbowner.tbname
  where     col_a = :col_a_bind 
    and     col_b = :col_b_bind
    and     col_c = :col_c_bind
  ;
  exit
EOF
  
  #echo 'run_the_sql - end  ' `date`
}


#
## Main processing
#
clear

echo 'start : ' `date`
`rm report.txt`

temp_ctr=0

while IFS=',' read -r -u3 acct_num
do  
   col_a_os=`expr substr $acct_num 1 3`
   col_b_os=`expr substr $acct_num 4 3`
   col_c_os=`expr substr $acct_num 7 4`
   run_the_sql
   temp_ctr=temp_ctr+1
   if  [temp_ctr -gt 1000 ]; then
       print '1000 done' `date`
       temp_ctr=0
   fi
done 3< list_of_tf.txt
echo 'end   : ' `date`

Identify the current version/level of DB2 z/OS via an SQL statement

After the database has been upgraded to DB2 12, you can use the below SQLs to identify the current level:

select catalog_level from sysibm.sysdummy1;                   
---------+---------+---------+---------+---------+---------+--
CATALOG_LEVEL                                                 
---------+---------+---------+---------+---------+---------+--
V12R1M500                                                     
DSNE610I NUMBER OF ROWS DISPLAYED IS 1                        
DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL, SQLCODE IS 100   
---------+---------+---------+---------+---------+---------+--

Another option:

SELECT GETVARIABLE('SYSIBM.VERSION') FROM SYSIBM.SYSDUMMY1    
---------+---------+---------+---------+---------+---------+--
                                                              
---------+---------+---------+---------+---------+---------+--
DSN12010                                                      
DSNE610I NUMBER OF ROWS DISPLAYED IS 1                        
DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL, SQLCODE IS 100   

Running an SCP in background while providing a password at the command prompt

I often have the requirement to run an SCP in the background (i.e. not terminally attached) on servers that have not exchanged keys to facilitate password less access. Hence, there is a need to provide a password in the foreground.

To put the SCP into background, enter the SCP command as follows (No “&” at end):

nohup scp -p /source_directory/my_very_large_file.bus myuser@target_server.somedomain.com:/target_directory > nohup.out 2>&1

You will be prompted for the password:

[unix_prompt] nohup scp -p /source_directory/my_very_large_file.bus myuser@target_server.somedomain.com:/target_directory > nohup.out 2>&1
myuser@target_server.somedomain.com's password: 

Enter the password and press enter
Then press CTRL Z

Output will look like this. You can see the CTRL Z and a message that states it is stopped:

[unix_prompt] nohup scp -p /source_directory/my_very_large_file.bus myuser@target_server.somedomain.com:/target_directory > nohup.out 2>&1
myuser@target_server.somedomain.com's password: 
^Z
[8]+  Stopped                 nohup scp -p /source_directory/my_very_large_file.bus myuser@target_server.somedomain.com:/target_directory > nohup.out 2>&1

After you see the stopped message and are back at the prompt type in “bg”:

[unix_prompt] bg
[8]+ nohup scp -p /source_directory/my_very_large_file.bus myuser@target_server.somedomain.com:/target_directory > nohup.out 2>&1 &
[unix_prompt]

The SCP will now be resumed in background:

[unix_prompt] ps -ef | grep myuser@target_server
oracle   42065 54077  1 18:16 pts/2    00:00:02 scp -p /source_directory/my_very_large_file.bus myuser@target_server.somedomain.com:/target_directory
oracle   55618 54077  0 18:18 pts/2    00:00:00 grep myuser@target_server

The SCP is now running in the background and the terminal is freed.

WHILE loop stops processing after first line of input

I had the below input file and a script whose logic was to process each line of the input file. Within the logic, I needed to perform an OS command such as the “more /etc/oratab” command below.

Contents on the input file:

[server_name] more temp.txt
a
b
c

Code

#! /bin/bash
function perform_logic
{
  echo '***'
  temp=`more /etc/oratab | grep -i dean | wc -l`
}
  
while IFS=',' read -r service lsnr port
do
   echo $service $lsnr $port 
   perform_logic
done < temp.txt

I was expecting three lines of output; one for each line in the input file. However, the output was:

[server_name] ./temp.ksh
a
***
[server_name] 

Basically processing stops after the first line of the input file. The issue is that the “more /etc/oratab” is overlaying the standard input/output of the while command. After I changed the code to:

#! /bin/bash
function perform_logic
{
  echo '***'
  temp=`more /etc/oratab | grep -i dean | wc -l`
}
  
while IFS=',' read -r –u3 service lsnr port
do
   echo $service $lsnr $port 
   perform_logic
done 3< temp.txt 

Now the output displays that all lines of the input file are processed:

[server_name] ./temp.ksh
a
***
b
***
c
***
[dt2ompd2-splunk2]

#! /bin/bash
clear

function perform_logic
{
  echo '***'
  temp=`more /etc/oratab | grep -i dean | wc -l`
}
  
while IFS=',' read -r -u3 service lsnr port
#while IFS=',' read -r service lsnr port
do
   echo $service $lsnr $port 
   perform_logic
done 3< temp.txt 
#done < temp.txt

[dt2ompd2-splunk2] ./temp.ksh
a
***
[server_name]

Inserts disappearing from a table?

An offshore user contacted me today about an ongoing issue. Apparently the insert statements that his process was performing were disappearing from the table. We stepped through his java code and the code looked fine; the insert was occurring and was successful, no exceptions etc.

We ran a trace on the session and see the insert being executed.

TKPROF: Release 11.2.0.4.0 - Development on Wed Feb 26 11:52:46 2020
 
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
 
Trace file: PRDA11_ora_8025.trc
Sort options: default
 
********************************************************************************
count    = number of times OCI procedure was executed
cpu      = cpu time in seconds executing 
elapsed  = elapsed time in seconds executing
disk     = number of physical reads of buffers from disk
query    = number of buffers gotten for consistent read
current  = number of buffers gotten in current mode (usually for update)
rows     = number of rows processed by the fetch or execute call
********************************************************************************
 
SQL ID: dgya487q14n43 Plan Hash: 0
 
insert into ACCTOWN.ACC_CUST (ACCOUNT_NUMBER, STATUS, ACTIVATE_DATE, 
  CUST_ID) 
values
 (:1, :2, :3, :4)

However, the row was missing from the table.

My suspicion was that someone or some process was deleting the data from the table. In order to confirm this, I turned on auditing on the table:

AUDIT all ON ACCTOWN.ACC_CUST by ACCESS;

Auditing this database is set to write to the database.

show parameter audit;
audit_trail          string  DB

At this point I asked the user to run the process again and then checked the audit table:

alter session set nls_date_format = 'dd-MON-yy hh24.mi.ss';
select    os_username, username, userhost, 
          timestamp, owner,obj_name,action_name
from      dba_audit_trail
where     obj_name = 'ACC_CUST'
  and     action_name not in ('SELECT','SESSION','SESSION REC')
order by  timestamp
;
 

And I was able to confirm that a different process from the same application was deleting the data:

os_username username userhost timestamp          owner   obj_name action_name
ACCTOWN     ACCTOWN  srsr012  26-FEB-20 10.08.42 ACCTOWN ACC_CUST INSERT
kundjo4     ACCTOWN  srvr012  26-FEB-20 10.08.46 ACCTOWN ACC_CUST DELETE

After this was confirmed, turn off auditing on the table:

NOAUDIT ALL ON ACCTOWN.ACC_CUST;

Migrating a database from DB2 LUW to Oracle via SQL Developer

We are currently in the process of migrating our applications from DB2 LUW to Oracle and needed a method to do this without any investment in third party software or tools. After some experimentation, we settled on SQL Developer as the free tool of choice. I was going to write up the approach; however, my colleague Praveen Somisetty went above and beyond and created an excellent video explaining the process on his YouTube channel. The video can be viewed at:

A better understanding of the Oracle result cache

I had a flawed understanding of the result cache in that I felt it was used as a method to bypass an expensive access of the data blocks. In other words, Oracle expends resources to fetch a result set and then caches it for future users obviating the need to repeat the expensive data retrieval. A colleague questioned this and demonstrated that the actual methodology seems to be different. All of this was done on an 11GR2 instance.

What is the results cache?
It is an area in the shared pool that Oracle writes the results of the query. The goal is to avoid reading the data again and thus returning it faster to the user who runs the same SQL.

How do I control what goes into the results cache?
This is controlled via the RESULT_CACHE_MODE parameter. This parameter has two possible values

MANUAL – This is the default mode. The users are responsible for deciding what when to use the cache. This is controlled via the use of the /*+ result_cache */ hint.

FORCE – Oracle adds it to all possible statements

For the purpose of this write up, the RESULT_CACHE_MODE is set to MANUAL.

Assume that user TOM creates a table, adds data and then runs a select against that table. The first select will access the table and this can be confirmed by checking the execution plan. The second select of the table will also access the table. For the third execution of the select, add in the /*+ result_cache */ hint. Checking the execution plan will now reveal that the data was returned from the results cache.

Now switch users to BOB, set up a synonym pointing to TOM’s table and run the same SQL without a hint. This execution will access the table. Add in the hint and run the SQL and the data will be fetched from the cache.

V$RESULT_CACHE_OBJECTS displays all the objects (both cached results and dependencies) and their attributes. You can confirm that the same SQL used the results cache in response to the hint by checking the column SCAN_COUNT. This column is incremented every time the results cache is used to satisfy the SQL.

Invalidating the cache
If the data in the table is updated, the next execution of the SQL with the hint will not use the results cache. A new row will be added to the V$RESULT_CACHE_OBJECTS table and future executions of the SQL with the hint will cause the SCAN_COUNT to be incremented on this new row.

Issues with storing the password file in ASM under Oracle 12.1

We were copying the password file into ASM as this was supposed to allow us to have the password file in one location on a multi-node RAC database. In the past the practice has been to have the same version/copy of the password file was in the oracle home in each node of the RAC. Moving to a single password file on ASM seemed like a much better solution.

The odd result that we encountered was that when we would issue the below command at the asmcmd prompt

pwcopy --dbuniquename PRODDB /my_backup_location/pwdproddb +DG/proddb/password/pwdproddb

We would then list the files and see

+DG/ASM/password/pwdasm

We opened an SR with Oracle and were pointed to the below bug

Bug 19777340 - asmcmd pwcopy creates DB password file as incorrect fully qualified file name if destination is ASM diskgroup (Doc ID 19777340.8)

Per Oracle, the solution is to go to 12.2.01.

Application error causing MAX PROCESSES to be exceeded

We had a situation where an issue in the application was causing a large number of threads to be spawned from Apache/Tomcat. These threads would persist in an inactive status until we hit the max processes limit and then impact new threads.

As a quick fix, we changed the user’s profile to set the IDLE_TIME at 60 minutes. We did see the sessions being snipped after the IDLE_TIME threshold was reached:

USERNAME                       STATUS     COUNT(*)                                                  
------------------------------ -------- ----------                                                  
XYZ                            INACTIVE         10                                                  
PRDUSR                         ACTIVE            1                                                  
PRDUSR                         INACTIVE         44                                                  
PRDUSR                         SNIPED           20                                                  
SYS                            ACTIVE            1                                                  
SYS                            INACTIVE          1                                                  
                               ACTIVE           35

However, PMON was not actually killing the sessions resulting in us continuing to hit max processes. Oracle support suggested that this was due to “Bug 16494960 – killed session not being cleaned up (Doc ID 16494960.8)”.

Adding the keyword “immediate” resolved this as shown below:

alter system kill session '443,16740' immediate;

Since we tended to hit max processes in the early hours of the morning, I created the below script to monitor, report on and kill the sessions in an ‘INACTIVE’,’SNIPED’,’KILLED’ status:

#!/bin/ksh
cd /dba_scripts
export ORACLE_HOME=/oracle_home/12.1.0/
export PATH=$PATH:/oracle_home/12.1.0/bin
export ORACLE_SID=proddb1
sqlplus -s / as sysdba << EOF
   SET NEWPAGE NONE
   set linesize 100
   SET HEADING ON
   spool /dba_scripts/dean_info_before.txt
   select    username, status, count(*) 
   from      gv\$session 
   group by  username, status 
   order by  username, status
   ;   
   select    username, status, last_call_et, sid, serial# 
   from      v\$session 
   where     username = 'PRDUSR' 
     and     status in ('INACTIVE','SNIPED','KILLED') 
     and     last_call_et > 6000
   order by  sid, serial#  
   ;
   spool off
   SET HEADING OFF
   spool /dba_scripts/dean_kills.sql
   select    'alter system kill session '''||sid||','||serial#||''' immediate;' as script 
   from      v\$session 
   where     username='PRDUSR' 
     and     status in ('INACTIVE','SNIPED','KILLED') 
     and     last_call_et > 6000
   order by  sid, serial#  
   ;
   spool off
   SET HEADING ON
   spool /dba_scripts/dean_killed.log
   set echo on
   set feedback on
   @@/dba_scripts/dean_kills.sql
   spool off
   spool /dba_scripts/dean_info_after.txt
   select    username, status, count(*) 
   from      gv\$session 
   group by  username, status 
   order by  username, status
   ;   
   spool off
   exit
EOF
echo 'Status before kills'  > dean_report.txt
echo '-------------------' >> dean_report.txt
echo ' ' >> dean_report.txt
cat /dba_scripts/dean_info_before.txt >> dean_report.txt
 
echo ' ' >> dean_report.txt
echo ' ' >> dean_report.txt
echo ' ' >> dean_report.txt
echo 'Sessions identified for kills' >> dean_report.txt
echo '-----------------------------' >> dean_report.txt
echo ' ' >> dean_report.txt
cat /dba_scripts/dean_kills.sql >> dean_report.txt
 
echo ' ' >> dean_report.txt
echo ' ' >> dean_report.txt
echo ' ' >> dean_report.txt
echo 'Kill output' >> dean_report.txt
echo '-----------' >> dean_report.txt
echo ' ' >> dean_report.txt
cat /dba_scripts/dean_killed.log >> dean_report.txt
 
echo ' ' >> dean_report.txt
echo ' ' >> dean_report.txt
echo ' ' >> dean_report.txt
echo 'Status after kills' >> dean_report.txt
echo '-------------------' >> dean_report.txt
echo ' ' >> dean_report.txt
cat /dba_scripts/dean_info_after.txt >> dean_report.txt
 
mail -s "Kills from proddb1" "My-email-id@mydomain.com" < /dba_scripts/dean_report.txt

This was then scheduled in cron as follows:

#
## Oct 04, 2019 - Dean Capps - Start
##     This scripts kills threads to stay under the 900 processes limit
#   
00 00,12 * * * * cd /dba_scripts; ksh ./dean_kills.ksh > /dba_scripts/dean_kills.ksh.cron.out 2>&1
#
## Oct 04, 2019 - Dean Capps - End
#

The output will be as follows:

Status before kills
-------------------
 
USERNAME                       STATUS     COUNT(*)                                                 
------------------------------ -------- ----------                                                 
XYZ                            INACTIVE         10                                                 
PRDUSR                         ACTIVE            1                                                 
PRDUSR                         INACTIVE         44                                                 
PRDUSR                         SNIPED           20                                                 
SYS                            ACTIVE            1                                                 
SYS                            INACTIVE          1                                                 
                               ACTIVE           35                                                 
 
7 rows selected.
 
USERNAME                       STATUS   LAST_CALL_ET        SID    SERIAL#                         
------------------------------ -------- ------------ ---------- ----------                         
PRDUSR                         SNIPED           6199        443      16740                         
PRDUSR                         SNIPED           6199        556      37500                         
PRDUSR                         SNIPED           6199        615      17620                         
PRDUSR                         SNIPED           6199        670      46290                         
 
 
 
 
Sessions identified for kills
-----------------------------
 
alter system kill session '443,16740' immediate;                                                   
alter system kill session '556,37500' immediate;                                                   
alter system kill session '615,17620' immediate;                                                   
alter system kill session '670,46290' immediate;                                                   
 
 
 
 
Kill output
-----------
 
 
System altered.
 
 
System altered.
 
 
System altered.
 
 
System altered.
 
 
 
 
Status after kills
-------------------
 
USERNAME                       STATUS     COUNT(*)                                                 
------------------------------ -------- ----------                                                 
XYZ                            INACTIVE         10                                                 
PRDUSR                         ACTIVE            2                                                 
PRDUSR                         INACTIVE         43                                                 
PRDUSR                         KILLED            1                                                 
PRDUSR                         SNIPED           16                                                 
SYS                            ACTIVE            1                                                 
SYS                            INACTIVE          1                                                 
                               ACTIVE           35                                                 
 
8 rows selected.

Oracle Recommendation – Do not use Alter user SYS to change the sys password with 12c

While supporting a number of databases there is often the need to change the SYS and other user’s passwords to stay in compliance with good security practices. To make it easier to change the passwords for multiple users on multiple databases, I created a C# solution that reads in a list of databases and issues the below commands to change the SYS, SYSTEM and DBSNMP passwords:

alter user SYS    identified by a_new_password;
alter user SYSTEM identified by a_new_password;
alter user DBSNMP identified by a_new_password;

In 11g and below, my C# solution would run the alter command on every instance of a RAC. If the RAC had a physical standby (Data Guard), I would then FTP the password file from the $ORACLE_HOME/dbs directory on one of the primary nodes to each of the standby nodes.

In the case of 12c RAC environment, the password file is stored within ASM and shared with all the nodes of the RAC. I modified the C# solution to issue the alter user SYS on only one node. This seemed to work in many instances but we did encounter issues in some cases. A colleague pointed me to Oracle support document:

“Should ALTER USER Statement Be Used For Changing SYS Password? (Doc ID 2452286.1)”

The recommendation is:

From an Oracle Database directly, ‘alter user username identified by password’ statement works for all users. But for SYS user it is advised to use the oracle ORAPWD utility to change its password. Though alter user statement should also work, but it may cause future unforeseen issues as well.

Also goes on to state “You may have to bounce the database for this (recommended).”

This applies to version 12.1.0.1 to 12.2.0.1 [Release 12.1 to 12.2]

142
51
73
77