Store SQL query string into variable BASH - sql

I have a select statement inside a bash script that returns the latest date in the DB. I run this query 4 times so I want to define it just once and assing the text to a variable.
#!/bin/bash
linux commands;
database_date=$(sqlplus -s/nolog $USER/$USER#BRMDPP <<END
set pagesize 0 feedback off verify off heading off echo off;
SELECT ...
exit;
END
)
commands that change the database date;
last_date=$(sqlplus -s/nolog $USER/$USER#BRMDPP <<END
set pagesize 0 feedback off verify off heading off echo off;
SELECT ...
exit;
END
)
commands that change the database date;
How can I store this big string $(sqlplus ... into one variable and use it again?
Thank you

One way would be to make use of a function:
foo() {
sqlplus -s/nolog $USER/$USER#BRMDPP <<END
set pagesize 0 feedback off verify off heading off echo off;
SELECT ...
exit;
END
}
and later invoke it by saying:
value=$(foo)
In order to get the value returned by the function, say echo "$value" (note that quoting variables is important).

Related

SQLPLUS use optional parameters

Currently, I call an SQL File from a CMD file and transfer some parameters during the call. The code below works if I actually pass a value or press enter. However, my CMD/SQL will stop and wait for the parameter if there was none given. In such a case I would like to continue with default values instead.
CMD Code:
REM this works
sqlplus !dbuser! #!some_dir!\some_sql_file.sql test_text >> !log!
REM this STOPS and waits until user interaction happens
sqlplus !dbuser! #!some_dir!\some_sql_file.sql >> !log!
SQL Code:
set serveroutput on
SET LINESIZE 10000
declare
l_some_text varchar2(1000);
begin
select nvl('&&3','no_text_given') into l_some_text from dual;
dbms_output.enable;
dbms_lock.sleep(1);
dbms_output.put_line('SQL uses: ' || l_some_text );
end ;
Parameter can't be 3; there's only one, so it is supposed to be 1.
Here's option you might be interested in.
a.sql file:
set serveroutput on
set ver off
set termout off
column 1 new_value 1
select null as "1" from dual where 1 = 2;
set termout on
declare
l_some_text varchar2(1000);
begin
select nvl('&1', 'no_text_given') into l_some_text from dual;
dbms_output.put_line('SQL uses: ' || l_some_text );
end ;
/
exit;
Let's test it: with parameter passed to it:
c:\temp>sqlplus -s scott/tiger#pdb1 #a.sql littlefoot
SQL uses: littlefoot
PL/SQL procedure successfully completed.
Without parameter:
c:\temp>sqlplus -s scott/tiger#pdb1 #a.sql
SQL uses: no_text_given
PL/SQL procedure successfully completed.
c:\temp>
Adjust it, if you have to (e.g. redirect to file; remove various settings I used, ...) but - generally - that's what you asked for.
In cmd,
if not defined parameter set "parameter=defaultvalue"
sqlplus !dbuser! #!some_dir!\some_sql_file.sql !parameter! >> !log!
if parameter has not been set, uses default. You haven't shown us how you provide test_text to the cmd procedure; you would need to assign its value to parameter

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 run sql script in an anonymous block?

Is it possible to run an sql script in an anonymous block?
I would like to be able to conditionally run an sql script.
#!/bin/bash
.
.
.
`sqlplus -S /nolog > log3 << EOFSQL
connect ${userName}/${userPassword}#${urlDataBase};
set serveroutput on
set pagesize 0
set feedback off
set verify off
set heading off
set wrap off
set linesize 150
DECLARE
lv_error VARCHAR2(100):='';
BEGIN
SELECT TRIM(STATUS) INTO lv_error FROM tab1
WHERE ...
IF lv_error NOT LIKE 'ERROR%' THEN
START $(pwd)/script.sql;
/
END IF;
END;
/
In your case it is better to write a PL/SQL procedure and invoke it from the shell script. That way, it is easier to conditionally call the second sql function/procedure.
Or, if you want to keep the logic in shell script, spool the output of the first SQL and then read it and call the second sql script.
Something like this.
#!/bin/bash
.
.
.
sqlplus -S /nolog > log3 << EOFSQL
connect ${userName}/${userPassword}#${urlDataBase};
spool spool_out.txt
set linesize 150
DECLARE
lv_error VARCHAR2(100):='';
BEGIN
SELECT TRIM(STATUS) FROM tab1
WHERE ...
END;
/
EOFSQL
errorPresent=`cat spool_out.txt|grep "ERROR"`
if [ ! -z $errorPresent ]
the
#Call $(pwd)/script.sql;
fi

variable value not assigned in cronjob

I have a code which when executed manually ( via this command i.e. ./script.sh ) assigns the query result to a variable but when it is executed automatically via crontab then the query result is not assigned to the variable.
Please find below the code:
diff=0;
diff=`sqlplus -s user/pswd#leadb2 << END
set pagesize 0 feedback off verify off heading off echo off;
SELECT (to_Date($timeRightNow,'YYYYMMDDHH24MISS') - to_date($timeoffile,'YYYYMMDDHH24MISS') ) * 24 * 60
difference_in_minutes
from dual;
END`
echo "Difference is $diff ">>/backup2/softdev/settlement/p.txt
It usually happens because your cron is not aware of your variables initialized in .profile. Even the path for sqlplus is not available, when you run this script via cron. So, to fix the issue, set the cron job in below format, and it will work fine.
00 * * * * . ~/.profile && /home/absolut_path_to_script.sh > /home/log_file_path.txt
i have done it as cron doesnot know the path of SQLPlus so it does not execute the query as soon as the path is given to cron the job responds perfectly
Here is the solution:
ORACLE_HOME=/oracle/app/oracle/product/11.2.0/db_1
export ORACLE_HOME
dateToday=date +%Y%m%d%H%M%S
diff=0;
timeRightNow='20170519180000'
timeoffile='20170519175500'
diff=$ORACLE_HOME/bin/sqlplus -s user/paswd#leadb2 << END
set pagesize 0 feedback off verify off heading off echo off;
SELECT (to_Date($timeRightNow,'YYYYMMDDHH24MISS') - to_date($timeoffile,'YYYYMMDDHH24MISS') ) * 24 * 60
difference_in_minutes
from dual;
END
echo "Difference is $diff ">>/backup2/softdev/settlement/p.txt

Pass shell variables to SQL statement

I want to pass shell variables to sql statement. Both shell script and SQL statement are present in the same script file.
I want the values of the variables retMonth, retLastDay and retPrvYear in the SQL statement.
Below is the code.
If I execute this, it prints - " partition_date between '01--' and '--' \ 0 0] 1 1] 12-DEC-14 1"
How can I have values of retMonth, retLastDay and retPrvYear in SQL statement?
echo $retMonth //This prints 07
echo $retLastDay //This prints 31
echo $retPrvYear //This prints 2015
count=$(sqlplus -s ${DBA_ORACLE_USER}/${DBA_ORACLE_PWORD}#${ORACLE_SID} <<END
#connect ${DBA_ORACLE_USER}/${DBA_ORACLE_PWORD}#${ORACLE_SID}
set serveroutput on
set linesize 1000
set heading off
set feedback off
define lastMonth=$retMonth
define lastYear=$retPrvYear
define lastDay=$retLastDay
SELECT count(1)
FROM MYTABLE
WHERE partition_date between '01-$lastMonth-$lastYear' and '$lastDay-$lastMonth-$lastYear'
);
END
)
Try using quoted shell variables directly without using define directives:
count=$(sqlplus -s "${DBA_ORACLE_USER}/${DBA_ORACLE_PWORD}#${ORACLE_SID}" <<END
set serveroutput on
set linesize 1000
set heading off
set feedback off
SELECT count(1)
FROM MYTABLE
WHERE partition_date between
"01-$retMonth-$retPrvYear" and "$retLastDay-$retMonth-$retPrvYear";
END
)