simple Shell Script - howto do the same in PL/SQL - sql

I have a simple shell script with SQL code which does:
generate with SQL*Plus (SQL statement) a batch file
checks if output from SQL*Plus more than 400 lines (if more than 400 lines exit and writes mail to Operations team)
if less than 400 lines SQL*Plus output, executes the batch file automatically
This script works very well. I wish to write the same script with PL/SQL (without Shell code). Is this possible? Can you provide me the code (I am in process of learning PL/SQL).
Database is Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 on Solaris.
#!/bin/ksh
. /opt/db/scripts/setpath.sh
generate_batch ()
{
sqlplus -S $DBUSER/$DBPASSWD#$ORACLE_SID <<EOF > /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$(/bin/date '+%d%m%Y.%Hh')
set echo Off
set term On
set pages 0
set head off
set ver off
set feed off
set trims on
set linesize 20000
WITH data
AS (SELECT user_id,
jc_name,
upd_time,
RANK () OVER (PARTITION BY user_id ORDER BY upd_time ASC)
rk
FROM user_jc
WHERE user_id IN ( SELECT user_id
FROM user_jc
WHERE JC_NAME LIKE 'CFF\_S\_%' ESCAPE '\'
GROUP BY user_id
HAVING COUNT (user_id) > 1)
AND JC_NAME LIKE 'CFF\_S\_%' ESCAPE '\')
SELECT 'DISCONNECT ent_user FROM job_code WITH user_id = "'
|| user_id
|| '", jc_name = "'
|| jc_name
|| '";'
FROM data
WHERE rk = 1;
exit
EOF
}
sanity_check ()
{
line_nr=$(wc -l /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$(/bin/date '+%d%m%Y.%Hh') | awk ' { print $1 } ')
if [ $line_nr -gt 400 ]; then
(cat /opt/db/scripts/tools/delete_connection/mail_body.txt) | mailx -s "Alert: please manually execute /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$DATE" -r test#example.com test2#example.com
exit 1
fi
}
run_batch ()
{
/opt/bmchome/bin/ess batchrun -A -i /opt/db/scripts/tools/delete_connection/batchrun/batchrun.$(/bin/date '+%d%m%Y.%Hh')
}
generate_batch && sanity_check && run_batch

In PL/SQL, I'd do it the other way round:
Count number of connections that match your query
If result > 400 send email
Else generate the disconnection statements, probably with ALTER SYSTEM DISCONNECT SESSION...
I don't know your requirement of course, but could it be solved with resource profiles to limit user connections?
CREATE PROFILE myprofile LIMIT SESSIONS_PER_USER = 1;
ALTER USER myuser PROFILE myprofile;

Related

integer expression expected shell scripting - BASH

I am trying to get the LAG between Primary & Standby database using the below shell script. The query works fine returning the values "DATABASE IS OUTOFSYNC" or "DATABASE IS INSYNC" for an instance that has 1 Node which returns a single value, but I get an error "[: 0 1: integer expression expected" for an instance that has two Nodes which returns two values for the LAG on the first Node and the Second Node.
So here is the code:
#!/bin/bash
get_status=$(sqlplus -s "/as sysdba" <<EOF
set pagesize 0 feedback off verify off heading off echo off;
SELECT prim.seq - tgt.seq seq_gap
FROM
(
SELECT thread#, MAX(sequence#) seq, MAX(completion_time) tm
FROM
v\$archived_log
GROUP BY
thread#
)
prim,
(
SELECT thread#, MAX(sequence#) seq, MAX(completion_time) tm
FROM
v\$archived_log
WHERE
dest_id IN
(
SELECT
dest_id
FROM
v\$archive_dest
WHERE
target = 'STANDBY'
)
AND
applied = 'YES'
GROUP BY
thread#
)
tgt
WHERE
prim.thread# = tgt.thread#;
exit;
EOF
)
if [ "$get_status" -ge 5 ]; then
echo "DATABASE IS OUTOFSYNC"
else
echo "DATABASE IS INSYNC"
fi
Is there a better way to write this script?
After adding typeset -p get_status after the query and before the if I get the below results:
declare -- get_status=" 1
0"
./dgtest2.sh: line 41: [: 1
0: integer expression expected
DATABASE IS INSYNC
The query is returning more than one value/string (for 2 nodes or threads) as shown in picture/screenshot and it seems like my script is only coded to address a single value/string generated by the query.
enter image description here
Is there away to modify the script to address multiple values/strings generated by the query
The logic should be if all values returned are -ge 5 it should report "DATABASE IS OUTOFSYNC" else "DATABASE IS INSYNC" for all values returned are -lt 5.
The logic for one value -lt 5 and one value -ge 5 would not suffice as the values constantly change on the database.
Any values from 0 - 4 that the database returns whether from both Nodes should report as "DATABASE IS INSYNC" and any value from 5 upwards that the database returns whether from both Nodes should report as "DATABASE IS OUTOFSYNC".
One idea would be to capture the status values (returned by the sqlplus script) into an array and then loop through the array testing said status values.
Instead of:
variable=$(sqlplus ...)
We want:
variable=( $(sqlplus ...) )
For OP's current scripting, with a name change for the variable, we will replace this:
get_status=$(sqlplus -s "/as sysdba" <<EOF
set pagesize 0 feedback off verify off heading off echo off;
SELECT prim.seq - tgt.seq seq_gap
...
exit;
EOF
)
With this:
status_array=( $(sqlplus -s "/as sysdba" <<EOF
set pagesize 0 feedback off verify off heading off echo off;
SELECT prim.seq - tgt.seq seq_gap
...
exit;
EOF
) )
One idea for the follow-on logic testing:
default database status is INSYNC
if any status values are -ge 5 then set database status to OUTOFSYNC
The code for this looks like:
db_status='INSYNC'
for status in "${status_array[#]}"
do
[[ "${status}" -ge 5 ]] && db_status='OUTOFSYNC' && break
done
echo "DATABASE IS ${db_status}"
I'm not setup to run the sqlplus script but I should be able to simulate the results with the following array assignments:
status_array=(1)
status_array=(7)
status_array=(0 1)
status_array=(5 7)
status_array=(5 3)
Running our code for each of these array assignments gives us:
##################### status_array=(1)
DATABASE is INSYNC
##################### status_array=(7)
DATABASE is OUTOFSYNC
##################### status_array=(0 1)
DATABASE is INSYNC
##################### status_array=(5 7)
DATABASE is OUTOFSYNC
##################### status_array=(5 3)
DATABASE is OUTOFSYNC

How to pass arguments in IN clause of select statement as parameter having multiple values?

Iam writing a script in unix where where iam trying to implement the following
1) Connect to a database
2) run a select query and fetch the results in a file for validation
Now i have written the following
#!/bin/bash
file="./database.properties"
if [ -f "$file" ]
then
echo "$file found."
. $file
echo "User Id = " ${userName}
echo "user password = " ${password}
echo "schema = " ${schema}
sqlplus -S ${userName}/${password}#${schema}
set feedback off trimspool on
spool workflow_details.txt;
SELECT WORKFLOW_NAME, START_TIME, END_TIME, (END_TIME-START_TIME)*24*60 as TIME_TAKEN
FROM schema1.table1
WHERE WORKFLOW_NAME IN ('argument1,argument2,argument3,argument4')
AND WORKFLOW_RUN_ID IN (SELECT MAX(WORKFLOW_RUN_ID) FROM schema2.table3
WHERE WORKFLOW_NAME IN ('argument1'));
spool off;
exit;
else
echo "$file not found."
fi
The requirement is the value iam using in In clause i.e( argument1,argument2....etc.) is present in a file and the script should be modified such that the arguments will be fetched and placed in In clause through comma separation. The number of arguments is dynamic . How to modify the code.
In short I need to fetch the arguments for IN clause at run time from a file having the argument details . The file will look like having a single column consisting of arguments.
As mentioned in my comments you need to use Collection to fulfill your requirement. See below demo and explanation inline.
In PLSQL
-- Declare a Nested table of type Number. You can declare it of type of your argument1,argument2..
Create or replace type var is table of number;
/
DECLARE
v_var var := var ();
v_num number;
BEGIN
--Fetching rows to collection
SELECT * BULK COLLECT INTO
v_var
FROM (
SELECT 1 FROM dual
UNION ALL
SELECT 2 FROM dual
);
--Printing values of collection
FOR rec IN 1..v_var.count LOOP
dbms_output.put_line(v_var(rec) );
END LOOP;
--Using in Where clause.
Select count(1)
into v_num
from dual where 1 Member of v_var; --<-- this is how you pass the collection of number in `IN` clause.
dbms_output.put_line(v_num );
END;
In your case: UNIX script
#!/bin/bash
#read from file and prepare the "in clause" --<--Put a loop to read through the file
in_clause=argument1,argument2 #--Prepare your `in_clause`
file="./database.properties"
if [ -f "$file" ]
then
echo "$file found."
. $file
echo "User Id = " ${userName}
echo "user password = " ${password}
echo "schema = " ${schema}
sqlplus -S ${userName}/${password}#${schema}
set feedback off trimspool on
spool workflow_details.txt;
SELECT workflow_name,
start_time,
end_time,
( end_time - start_time ) * 24 * 60 AS time_taken
FROM schema1.table1
WHERE workflow_name IN ($in_clause ) #<--Use in clause
AND workflow_run_id IN (SELECT MAX(workflow_run_id) FROM schema2.table3 WHERE workflow_name IN ( 'argument1' )
);
spool off;
exit;
else
echo "$file not found."
fi
PS: Not tested

Sessions are going Inactive status

I am running one sql query to fetch records from a table with multiple threads. Initially all the threads go in INACTIVE status and slowly it starts coming to ACTIVE status.
I have created HASH partition on my table based on the loc.
Also I have created an index over Item, loc columns.
select item || ',' || loc || ',' || stock_on_hand || ',' || on_order_qty || ',' || 0 || ',' || in_transit_qtyAS csv
from makro_m_oplcom_stg
where loc = ${loc} and ITEM_INV_IND = 0;
When I run this query in my DB for one loc it takes only one sec to fetch the records but when I am running it from shell script in multi threaded mode I am facing the session going into INACTIVE mode.
This is my wrapper script. Inside this I am calling another shell script that has the above mentioned query.
while [ $thread -le $SLOTS ] do
sleep 1
LOG_MESSAGE "$pgm_name Started by batch_makro_oplcom_process for thread $thread out of $SLOTS by ${USER}"
( ksh $pgm_name.ksh $UP $SLOTS $thread
if [ $? -ne 0 ]; then
LOG_MESSAGE "$pgm_name failed for thread: $thread, check the $pgm_name error file."
exit 1
else
LOG_MESSAGE "$pgm_name executed successfully for thread: $thread." fi )
& let thread=thread+1
done

Pass variables from shell script to SQL

I have an SQL statement file 1.sql:
set pages 0
set head off
set feed off
select $1 from
(
select $1 from user_tab_partitions
where table_name = 'test'
order by partition_position desc
)
where rownum = 1;
and I would like to execute the same SQL statement in a shell script, 1.sh:
#!/bin/ksh
username="test"
passwrd="testpass"
partition_name=$1
partition_position=$2
PARTNAME=`sqlplus -s $username/$passwrd << EOT
#1.sql $1
exit
EOT`
echo $PARTNAME
PARTPOS=`sqlplus -s $username/$passwrd << EOT
#1.sql $2
exit
EOT`
echo $PARTPOS
--
So, basically what I'm doing is executing the same SQL but with different inputs.
and I don't know how to pass these variables from SHELL script to the SQL script.
What should I change in my code???!!
Thanks for your time!
/Hesi
You need to change your SQL Script from $1 to &1. The $1 will on work if you imbed the SQL into the actual script here doc.
set pages 0
set head off
set feed off
select &1 from
(
select &1 from user_tab_partitions
where table_name = 'test'
order by partition_position desc
)
where rownum = 1;

Escaping special character when SQL output to variable in shell script

Trying to assign output from SQL query on an object containing special characters to a variable in shell script.
Running directly on the database:
db2 -x 'select count(*) from <SCHEMA>."/BIC/TEST"'
11000
Yet when I include this in script I need to use double quotes as I am using variables passed into the sql. Using single quotes
Output=$(db2 -x 'select count(*) from ${_SCHEMA}."/BIC/TEST"')
echo -e "$Output"
Results in:
SQL20521N Error occurred processing a conditional compilation directive near
"_". Reason code="7". SQLSTATE=428HV
When I use double quotes I hit:
SQL0104N An unexpected token "'/BIC/TEST'" was found following "ount(*)
Tried to escape the double quotes using another set of double quotes:
db2 -x 'select count(*) from ${_SCHEMA}.""/BIC/TEST""'
But this doesn't seem to work in script. It works for tables where there is no special characters/requirement to encase in quotations.
Any help is appreciated.
The code below works fine for me, notice the escaped quotes. If it fails for you, you need to give more details of your DB2-version and the DB2-server operating system platform.
#!/bin/ksh
db2 connect to sample
(($? > 0 )) && print "Failed to connect to database" && exit 1
db2 -o- "drop table \"/bin/test\" "
db2 -v "create table \"/bin/test\"(a integer)"
(($? > 0 )) && print "Create table failed" && exit 1
db2 -v "insert into \"/bin/test\"(a) values(1),(2),(3),(4)"
(($? > 0 )) && print "insert rows failed" && exit 1
db2 -v describe table \"/bin/test\"
typeset -i count_rows=$(db2 -x "select count(*) from \"/bin/test\"" )
(($? > 0 )) && print "query count rows failed" && exit 1
print "\nRow count is: ${count_rows}\n"
db2 -o- connect reset