How to pass a parameter from shell script to SQL script? - sql

I have 2 scripts - one shell and one sql.
My shell script is similar to this:
export nbr=&1
runsql script_name.sql
Im trying to pass a parameter for nbr while running the script.
The corresponding sql script is as such:
insert into table1
select * from table2
where year='&1'
I get the error as below:
"enter value for year: old 22: where year='$1')
new 22: where year='commit')"

I'll show how to deal with it easily with an example based on my own scripts.
In this script I define a simple function run_sql(DESCR, SCRIPT, DATABASES), where
DESCR - short description
SCRIPT - sqlplus run arguments, ie script name and its parameters
DATABASES - list of dbname defined above on which you want to run it
Then we can easily use it like this:
run_sql "1st script" "#sql1.sql param1" db1 db2
run_sql "2nd script" "#sql2.sql param1 param2 param3" db1 db2 db3
Here we execute:
"sql1.sql" with 1 argument on db1 and db2
"sql2.sql" with 3 arguments on db1,db2 and db3
And we save all output into own log files.
Full example with test output: https://gist.github.com/xtender/465951befeed7f0ae1a3fe112dcd7fe4
Simple script test.sh:
#!/bin/bash
# Here you can define your db connection strings:
db1=xtender/pass#PDB19C_11
db2=xtender/pass#PDB19C_11
db3=xtender/pass#PDB19C_11
#####################################################################
# functions:
# Function syntax: run_sql(DESCR, SCRIPT, DATABASES)
# where
# DESCR - short description
# SCRIPT - sqlplus run arguments, ie script name and its parameters
# DATABASES - list of dbname defined above on which you want to run it
run_sql(){
local DESCR="$1"; shift
local SCRIPT="$1"; shift
local databases=("$#")
echo =================================================
echo = $DESCR
echo = Going to execute $SCRIPT...
read -a res -p "Enter 'skip' to skip this step or press Enter to execute: "
if [[ $res = "skip" ]]
then
echo Skipping $SCRIPT...
else
echo Executing $SCRIPT...
for db in "${databases[#]}"
do
local cur=${!db}
echo Executing $SCRIPT on $db - $cur...
sqlplus -L -S ${cur} $SCRIPT >>log-$db.log 2>&1
echo Done.
done
echo =================================================
fi
}
#####################################################################
# Here we execute a script "sql1.sql" with one argument "param1" on db1 and db2:
run_sql "1st script" "#sql1.sql param1" db1 db2
# Here we execute a script "sql2.sql" with 3 arguments on db1,db2 and db3:
run_sql "2nd script" "#sql2.sql param1 param2 param3" db1 db2 db3
echo ============================================
echo === Done
echo ============================================
Then we can create sql scripts, for example sql1.sql and sql2.sql: sql1.sql requires one argument and sql2.sql - 3 arguments:
sql1.sql:
select '&1' as output from dual;
exit;
sql2.sql:
select
'&1' out1,
'&2' out2,
'&3' out3
from dual;
exit;

Related

DB2 Output to Variable via bash script

I'm hoping someone can help with applying the output from a db2 command to a variable to use later on in a script.
So far I am at...
db2 "connect to <database> user <username> using <password>"
while read HowMany ;
do
Counter=$HowMany
echo $HowMany
done < <(db2 -x "SELECT COUNT(1) FROM SYSCAT.COLUMNS WHERE TABNAME = 'TableA' AND TABSCHEMA='SchemaA' AND GENERATED = 'A'")
When trying to reference $Counter outside of the while loop, it returns SQL1024N A database connection does not exist. SQLSTATE=08003 as does the echo $HowMany
I've tried another method using pipe, which makes the $HowMany show the correct value, but as that is a sub shell, it's lost afterwards.
I'd rather not use temp files and remove them if possible as I don't like left over files if scripts abort at any time.
The DB2 CLP on Linux and UNIX can handle command substitution without losing its database connection context, making it possible to capture query results into a local shell variable or treat it as an inlined block of text.
#!/bin/sh
# This script assumes the db2profile script has already been sourced
db2 "connect to <database> user <username> using <password>"
# Backtick command substitution is permitted
HowMany=`db2 -x "SELECT COUNT(1) FROM SYSCAT.COLUMNS WHERE TABNAME = 'TableA' AND TABSCHEMA='SchemaA' AND GENERATED = 'A'"`
# This command substitution syntax will also work
Copy2=$(db2 -x "SELECT COUNT(1) FROM SYSCAT.COLUMNS WHERE TABNAME = 'TableA' AND TABSCHEMA='SchemaA' AND GENERATED = 'A'")
# One way to get rid of leading spaces
Counter=`echo $HowMany`
# A while loop that is fed by process substitution cannot use
# the current DB2 connection context, but combining a here
# document with command substitution will work
while read HowMany ;
do
Counter=$HowMany
echo $HowMany
done <<EOT
$(db2 -x "SELECT COUNT(1) FROM SYSCAT.COLUMNS WHERE TABNAME = 'TableA' AND TABSCHEMA='SchemaA' AND GENERATED = 'A'")
EOT
As you have found, a DB2 connection in one shell is not available to sub-shells. You could use a sub-shell, but you'd have to put the CONNECT statement in that sub-shell.
So it's more of a simple rewrite, and don't use a sub-shell:
db2 "connect to <database> user <username> using <password>"
db2 -x "SELECT COUNT(1) FROM SYSCAT.COLUMNS WHERE TABNAME = 'TableA' AND TABSCHEMA='SchemaA' AND GENERATED = 'A'" | while read HowMany ; do
Counter=$HowMany
echo $HowMany
done

How to pass a query stored in a variable to a sql file. Shell script

I am creating a shell script where I have saved entries from a text file into an array. Those values are properly stored and show the correct contents. One of those entries contains a simple query and I want to pass it to a sql file. With that sql query I want to save the results into a text file.
Here is the part of the code that calls the sql file to run the sql script
PURGE_SITES=purge_site.txt
logmsg "USERID - $PURGES_SITE" n
QUERY=${Unix_Array[4]}
echo $QUERY
sqlplus -s $USER/$PASS <<EndSQL
#purges_sites.sql $PURGE_SITES '$QUERY'
EXIT SQL.SQLCODE
EndSQL
for now query stored in ${Unix_Array[4]} is "select -1 from dual"
Here is the file contents of the .sql file
set echo off ver off feed off pages 0
accept fname prompt 'Loading Sites...'
spool &1;
&2
/
spool off
It gives me error and reads &2 as "&2" instead of the query saved in the variable. However when i edit the .sql file and add something beforehand, it will display the correct data from the variable.
Here is the output
select -1 from dual
File Name===> results.txt
select -1 from dual
Loading Sites...SP2-0042: unknown command "&2" - rest of line ignored.
SP2-0103: Nothing in SQL buffer to run.
Here is the output if I add something before &2.
select -1 from dual
File Name===> results.txt
select -1 from dual
Loading Sites...select * from table_table select -1 from dual
*
ERROR at line 1:
ORA-00933: SQL command not properly ended
I typed in select * from table_table before &2.
So its actually retrieving the value from the variable but something needs to come beforehand in order to pass correctly.
Is there a system execute command in oracle that will execute a query? &2 just by itself is not allowed.
Wont this help you?
PURGE_SITES=purge_site.txt
logmsg "USERID - $PURGES_SITE" n
QUERY=${Unix_Array[4]}
echo $QUERY
# FRAME YOUR QUERY, PROMPTING USER IN SHELL ITSELF AND SEND TO SQLPLUS DIRECTLY
# BEWARE SQL INJECTION POSSIBLE
# YOU CAN REDIRECT THE SQLPLUS OUTPUT TO A FILE LIKE THIS, NO SPOOL NEEDED
sqlplus -s $USER/$PASS <<EndSQL >> $OUTPUT_FILE
set echo off ver off feed off pages 0
$QUERY
/
EXIT SQL.SQLCODE
EndSQL

How to capture sqlplus command line output in unix file

I am running below : sqlplus ABC_TT/asfddd#\"SADSS.it.uk.hibm.sdkm:1521/UGJG.UK.HIBM.SDKM\"
afte that I am executing one stored procedure exec HOLD.TRWER
I want to capture return code of the above stored procedure in unix file as I am running the above commands in unix. Please suggest.
I guess you are looking for spool
SQL> spool output.txt
SQL> select 1 from dual;
1
----------
1
SQL> spool off
Now after you exit. the query/stroed procedure output will be stored in a file called output.txt
If by return code you mean output then:
command > file
If by return code you mean exit status then:
command
echo "$?" > file
If you mean something else, let us know.
You can store command return value in variable
value=`command`
And then checking it's value
echo "$value"
For your case to execute oracle commands within shell script,
value=`sqlplus ABC_TT/asfddd#\"SADSS.it.uk.hibm.sdkm:1521/UGJG.UK.HIBM.SDKM\" \
exec HOLD.TRWER`
I'm not sure about the sql query, but you can get the returned results by using
value=`oraclecommand`.
To print the returned results of oracle command,
echo "$value"
To check whether oracle command or any other command executed successfully, just
check with $? value after executing command. Return value is 0 for success and non-zero for failure.
if [ $?=0 ]
then
echo "Success"
else
echo "Failure"
fi

Running oracle script as oracle user from a shell script that runs as root

I have a shell script that runs as root. I want the script to switch to oracle user, run sqlplus and run some .sql files.
I am trying to followung :
su - oracle << -EOF1 2>&1
sqlplus $user/$password << -EOF2
#oracle.sql;
#quartz.sql;
EOF2
EOF1
first of all i get stty: standard input: Inappropriate ioctl for device what does it mean ?
second, can someone explain to me how the redirect (should) work in this case ?
Thanks
Use:
if [ "$(id -un)" -eq "root" ]; then
exec su - oracle -c $0
fi
sqlplus <<EOF
blablabla
EOF
If your script potentially takes arguments, the solution will differ.
What this does it checking whether the user running is currently root. If so, it re-executes the script ($0) as user oracle instead.
But BTW, why does the script run as root in the first place?
su - oracle -c " echo 'select 1 from dual;
select 2 from dual;'| sqlpus / as sysdba "
if contain ' using following
su - oracle -c " echo \"select 1 from dual;
select 2 from dual;\" | sqlpus / as sysdba "

Return a scalar query result into a batch file variable

I would like to perform a scalar database query and return the result into a variable in a batch file.
How would one do this? The closest I example in our system that I see is if I return an exit code based on a scalar query result.
Z:\SQL2005\90\Tools\Binn\sqlcmd -S servername -dCLASS -E -Q "EXIT(select case run_type when 'Q' then 200 else 100 end from cycle_date where cycle = '1')">NUL
if %errorlevel% == 200 call %SQLSERVER%
QRTLY.BAT
if %errorlevel% == 100 call %SQLSERVER%
MTHLY.BAT
Can someone help me with the syntax?
Here's some sqlcmd help info:
-v var = value[ var=value...]
Creates a sqlcmdscripting variable that can be used in a sqlcmd script. Enclose the value in quotation marks if the value contains spaces. You can specify multiple var="values" values. If there are errors in any of the values specified, sqlcmd generates an error message and then exits.
sqlcmd -v MyVar1=something MyVar2="some thing"
sqlcmd -v MyVar1=something -v MyVar2="some thing"
-x disable variable substitution
Causes sqlcmd to ignore scripting variables. This is useful when a script contains many INSERT statements that may contain strings that have the same format as regular variables, such as $(variable_name).
How about saving it to a file without headers then reading the contents back in?
sqlcmd -S(local)\SQLExpress -dMyDatabase -Umyuser -Pmypassword -W -h -1 -Q "SELECT Top 1 MyValue FROM MyTable" -o sqlcmdoutput.txt
set /p x= <sqlcmdoutput.txt
del sqlcmdoutput.txt
echo My scalar value is %x%
I use this in a batch file. It returns the LogicalFilename for a SQL Server Database data file. This only works if there is one data file in the DB.
So the result is the environment variable DATABASEFILENAME is set to say AdventureWorks_Data.
FOR /F "usebackq tokens=1" %%i IN (`sqlcmd -w200 -h-1 -E -Q"set nocount on; Select df.name From sysdatabases as d Inner Join sysaltfiles as df on d.dbid=df.dbid Where d.name ='$(DatabaseName)' and df.Fileid =1"`) DO set DATABASEFILENAME=%%i
Have you looked at sqlcmd?