Variable won't resolve inside PL-SQL statement - shell

I am trying to pass a SQL command to the SQL*Plus command-line program (Oracle) in my KornShell (ksh) script, but my $MY_VAR variable seems to be failing to resolve. $MY_VAR is a string value.
sqlplus -s << EOF >> $LOG_FILE
$MY_SCHEMA
UPDATE my_table SET run_flag = 'I', start_time = to_char(sysdate, 'DD-Mon-YYYY HH24:MI:SS') WHERE (process_id = '$MY_VAR' AND run_flag != 'F');
COMMIT;
EOF
I can successfully echo out the $MY_VAR variable, and so I can see that the variable is populated, but it does not seem to be resolving when inserting the variable into the SQL command which I am providing as an argument to the SQL*Plus program. The log file for the script simply outputs:
0 rows updated. Commit complete.
The SQL seems to be valid as we can successfully execute the command in SQL Developer (albeit with a hardcoded value for $MY_VAR).
Any ideas on what I am missing here?
Thank you

Try this:
sql="$MY_SCHEMA
UPDATE my_table SET run_flag = 'I', start_time = to_char(sysdate, 'DD-Mon-YYYY HH24:MI:SS') WHERE (process_id = '$MY_VAR' AND run_flag != 'F');
COMMIT;"
print "$sql"
sqlplus -s <<<"$sql" >> $LOG_FILE
Does the sql look correct? If it does and zero rows are updated, your where clause must be selecting zero rows.
Your version of ksh may not have the <<< here-string syntax. In that case:
print "$sql" | sqlplus -s >> $LOG_FILE

Related

How to get plain formatted output in csv with shell script for oracle sql query

This is my shell script.
echo "Start";echo #/opt/apps/Tests/SQLDir/Test1.sql | sqlplus Db1/Db1#//maydomain:port/abc;echo "Finish";
echo "Start";echo #/opt/apps/Tests/SQLDir/Test2.sql | sqlplus Db1/Db1#//maydomain:port/abc;echo "Finish";
I have 30 .sql files like this, added in one .sh file which results 30 .csv files
Test1.sql has
SPOOL /opt/apps/Tests/OF/output1.csv REPLACE;
select name from username where id = 10 and Sname is not NULL and ROWNUM < = 50000;
Test2.sql has
SPOOL /opt/apps/Tests/OF/output2.csv REPLACE;
select strname,ctyname from addr where city = 'NYC' and ROWNUM < = 50000;
My expected OP in output1.csv is
name
Abc
xyz
pqr
My expected OP in output2.csv is
strname | ctyname
10-AP NYC
11-KP MCH
90-ZP SDK
right now I am getting weird o/p in csv
name
-------------------------------
Abc
xyz
pqr
name
-------------------------------
TYU
KLH
50000 rows selected.
SQL>
So is there any way to remove those additional lines [--------- and 50000 rows selected.] with shell script/sql code?
And while executing shell script all sql result rows are getting printed on screen. how to avoid that?
Thanks in advance.
Following SQL*Plus command should do the job:
set markup csv on delimiter ' ' quote off
set feedback off
I must say, your method of passing the name of a script to sqlplus is the strangest I've ever seen. The usual practice (given your names) would be:
sqlplus Db1/Db1#//maydomain:port/abc #/opt/apps/Tests/SQLDir/Test1.sql
I don't see where your 'echo Start' and 'echo finished' accomplish anything, since there is no clarifying info coming along with it.
It looks to me like what you want in your scripts would be
set echo off trimsp on head off pagesize 0
spool /opt/apps/Tests/OF/output2.csv replace
select strname,ctyname from addr where city = 'NYC' and ROWNUM < = 50000;
spool off
BTW, 'spool' is a sqlplus command - a directive to sqlplus itself, not a sql statement. As such, it does not need a semi-colon at the end.
-- edit
Example of using environment variables on sqlplus command line:
username=scott
userpw=tiger
server=myserver
port=1521
dbname=mydb
sqlplus $username/$userpw#//$server:$port/$dbname
Though I would question why you need to set them as variables.
And I prefer to use tnsnames rather than ezconnect.

How to fetch more than one column value from oracle select query to shell variable

I am trying to fetch a row with more than one column value to different shell variables. Infact I found that at a time all the column values can be stored to single shell variable. But how can I put those column values to seperate shell variables. Below is an example I am trying for time being
function sqlQuery {
sqlplus -S shiyas/********* <<'EOF'
set heading OFF termout ON trimout ON feedback OFF
set pagesize 0
SELECT name,open_mode from v$database;
EOF
}
OUTPUT="$( sqlQuery )"
echo $OUTPUT
Here I am getting the output as
ORCL READ WRITE
But my requirement is column values ORCL, READ WRITE should get assigned to different shell variable.
I tried the below of parsing.
echo "$OUTPUT" | while read name open_mode
but it was throwing unexpected end of file error.
-bash-3.2$ sh call_sql_col_val_1.sh
ORCL READ WRITE
call_sql_col_val_1.sh: line 18: syntax error: unexpected end of file
Please let me know what concept I can use to fetch a single row column values to different shell variables.
I do this via eval myself:
oracle#******:/*****> cat test.sh
#!/bin/bash
function sqlQuery {
sqlplus -S / as sysdba <<'EOF'
set heading OFF termout ON trimout ON feedback OFF
set pagesize 0
SELECT name,open_mode from v$database;
EOF
}
eval x=(`sqlQuery`)
NAME=${x[0]}
OPEN_MODE="${x[1]} ${x[2]}"
echo NAME IS $NAME
echo OPEN_MODE IS $OPEN_MODE
So we are running the same function you have above, passing it into x and running it through eval to handle the delimitation. Then you have an array and call call is as such: x[0] for the first item, for example.
Output is:
oracle#******:/******> sh test.sh
NAME IS ******
OPEN_MODE IS READ WRITE

Check if value within column exists, if not create the column

I need to check if one of the columns in my db contains specific value. If it doesn't I want to create that row with folowing values:
#!/bin/bash
#
MODEL=$1
if true (SELECT * FROM table.STATISTICS
WHERE MODEL = '$MODEL' )
do this (INSERT INTO table.STATISTICS('$MODEL',0,SYSDATE,0,SYSDATE,0); )
You could use a merge for this, run through SQL*Plus as a 'heredoc', so you don't have to do a separate count operation; the merge will do that for you effectively:
#!/bin/bash
MODEL=$1
sqlplus -s /nolog <<!EOF
connect user/pass
merge into statistics s
using (select '${MODEL}' as model, 0 as num1, sysdate as date1,
0 as num2, sysdate as date2 from dual) t
on (s.model = t.model)
when not matched then
insert (s.model, s.num1, s.date1, s.num2, s.date2)
values (t.model, t.num1, t.date1, t.num2, t.date2);
!EOF
But using your real column names, obviously. It's better to list them explicitly even for a plain insert.
get_count () {
sqlplus -s username/pass <<!
set heading off
set feedback off
set pages 0
select count(model) from statistics
where model='$MODEL';
!
}
count=$(get_count $1)
if [ "${count:-0}" -eq 0 ]; then
echo "its zero"
sqlplus -S username/pass << EOF
whenever sqlerror exit 1;
set echo on
set verify off
INSERT INTO table.STATISTICS VALUES('$MODEL',0,SYSDATE,0,SYSDATE,0);
exit;
EOF
fi

open sqlplus connection only once in while loop

I have 100 records. When I am running the code below, the SQL*Plus connection is opening many times utilizing 100% of CPU. Is there any way in which I could open SQL*Plus connection only once, i. e. outside while loop?
**#!/bin/bash
export ORACLE_HOME=/software/oracle/ora10204
export PATH=$PATH:$ORACLE_HOME/bin
INPUT_FILE='file.csv'
IFS=','
i=0
while read name id do
a[i]="$name"
b[i]="$id"
echo "${a[$i]} ${b[$i]}"
set serveroutput on;
sqlplus .../...#'(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=...)(HOST=...)(PORT=...)))(CONNECT_DATA=(SID=...)))'<<EOF
insert into code_entry(inh_valu,edi_valu) values('${a[$i]}' , '${b[$i]}');
EOF
let i=$i+1
done < $INPUT_FILE**
You could create a SQL script, that you would invoke once in the end of your shell script with SQL*Plus. It would use INSERT ALL DML command, and you would just append the strings to it using your shell script and redirect operator (>>) within your existing loop.
After running your shell script your SQL script could look like this:
insert all
into code_entry (inh_valu, edi_valu) values (1, 'foo')
into code_entry (inh_valu, edi_valu) values (2, 'bar');
...
You would then just run it like this:
$ sqlplus scott/tiger #myscript.sql
A sample shell script could look like:
echo "insert all" > myscript.sql
INPUT_FILE='file.csv'
IFS=','
i=0
while read name id do
a[i]="$name"
b[i]="$id"
echo "into code_entry(...) values ('${a[$i]}', '${b[$i]}')" >> myscript.sql
let i=$i+1
done < $INPUT_FILE**
echo ";" >> myscript.sql
sqlplus .../...#'(DESCRIPTION=...)' #myscript.sql

Including pipe running Oracle sql from DOS batch script

We use the approach below to run SQL from a DOS batch script.
The model works fine but this specific code doesn't work. I believe because of the || characters. I tried using ^|^| but this didn't work.
Any ideas?
(
echo update mytable set file_path = 'C' || substr(file_path, 2);
echo commit;
echo exit
) | sqlplus x/x#orcl
Store the SQL as file and redirect SQL Plus's input:
sqlplus x/x#orcl <sql.txt
You can use CONCAT instead of the || operator
Escaping the || with ^|^| leaves you with yet another problem: cmd.exe thinks that the closing parenthesis of substr(file_path, 2); belongs to the opening parenthesis in the first line. It is therefore not printed to SQL*Plus, thus rendering the update statement to something like update mytable set file_path = 'C' || substr(file_path, 2 which obviously cannot be interpreted by Oracle.
You can solve this if you put the entire update statement into double quotes and feed this to (yet another) cmd.exe, like so:
(
#echo select * from mytable;
#cmd /c "echo update mytable set file_path = 'C' ^|^| substr (file_path, 2);"
#echo commit;
#echo exit
) | sqlplus x/x#orcl

Resources