Pass variables from shell script to SQL - 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;

Related

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

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

How to execute a query stored in a variable in SQL*Plus

Is there any way to pass a query to SQL*Plus through a variable?
I'm aware SQL*Plus has the capability to execute a file like:
sqlplus user/pass#db #filename
Within a kornshell script I'm trying to do:
query="select * from dual;"
sqlplus user/pass#db $query
There might have solution to do that BUT I can achieve the same goal using the following method.
[oracle#myserver Desktop]$ $ORACLE_HOME/bin/sqlplus -s jay/passsword#db <<!
select * from dual;
exit
!
D
-
X
Update, you can store the returned result in a variable as shown below.
query="select * from dual;"
var=$($ORACLE_HOME/bin/sqlplus -s jay/pass#db <<!
set pages 0
set head off
set feed off
$query
!
);

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
)

simple Shell Script - howto do the same in PL/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;