Iterating through PL/SQL result in SHELL - oracle

I want to iterate over PL/SQL rows in SHELL script and for each row I want to execute some code using current row. At this point I got:
VALUE='sqlplus -s /nolog <<EOF
CONNECT ${CONNECT}
select smth from table;
/
EXIT
EOF'
for i in "${VALUE[#]}"
do
##some code using "i" variable
done
At this point for 5 rows code executes only once. It appears it doesn't iterate at all. Any ideas how can I fix that?

You can iterate your resultset as follows:
SQL> select car_model from available_models
2 group by car_model ;
CAR_MODEL
------------------------------
Corsair
Executive
Country Squire
SQL>
Rewrite your shell script (using WHILE) as follows:
[oracle#ora12c 1]$ cat test.sh
CONNECT='z_test/welcome1'
VALUE=`sqlplus -s /nolog <<EOF
CONNECT ${CONNECT}
set head off
select car_model from available_models
group by car_model
/
EXIT
EOF`
echo "resultset: "
echo "${VALUE}"
echo " "
echo "now iterate ..."
let rec=0
echo "${VALUE}" |while read line
do
echo "processing $rec: $line"
let rec=rec+1
done
[oracle#ora12c 1]$
And this is the expected output:
[oracle#ora12c 1]$ ./test.sh
resultset:
Corsair
Executive
Country Squire
now iterate ...
processing 0:
processing 1: Corsair
processing 2: Executive
processing 3: Country Squire
Note that line #0 is blank and is expected, because is part of your resultset too.
Adding "set pagesize 0" will remove this blank line:
[oracle#ora12c 1]$ cat test.sh
CONNECT='z_test/welcome1'
VALUE=`sqlplus -s /nolog <<EOF
CONNECT ${CONNECT}
set head off
set pagesize 0
select car_model from available_models
group by car_model
/
EXIT
EOF`
......
Expected run:
[oracle#ora12c 1]$ ./test.sh
resultset:
Corsair
Executive
Country Squire
now iterate ...
processing 0: Corsair
processing 1: Executive
processing 2: Country Squire
[oracle#ora12c 1]$
Regards

Related

bash sqlcmd variable array

I´m trying to acces values in a variable I receive from an sqlcmd in a loop:
while read line ;do AllNumbers=`sqlcmd -h -1 -S mysqlserver.localdomain -U Me -P MyPasswd -d master -Q "SET nocount on; SELECT PHONENUMBER FROM MYTABLE WHERE USER LIKE '"$line"' " < /dev/null`; for i in "$AllNumbers"; do echo "$line $i"; done; done< mytext.txt
The Number of results in the variable "$AllNumbers" can be 1 to 5, and the lenght of the number can also vary.
What I get in Moment is:
John 01234567
9876543
Jack 13579024
Jim 08642135
10293847
56473829
In the end I want to have an output like:
John 01234567
John 98765432
Jack 13579024
Jim 08642135
Jim 10293847
Jim 56473829
I tried with IFS, but I´m unsure what the delimiter in the variable $AllNumber is.
If I do:
OneUser=`sqlcmd -h -1 -S mysqlserver.localdomain -U Me -P MyPasswd -d master -Q "SET nocount on; SELECT PHONENUMBER FROM MYTABLE WHERE USER LIKE '"Jim"' "`; echo "$OneUSer"
I get:
08642135
10293847
56473829
I´m glad for any hint...
Thanks in advance
Lotte
Why not ask the database to do the hard work?
SELECT USER, PHONENUMBER FROM MYTABLE WHERE USER LIKE '"$line"'
Also, make sure you validate $line to prevent SQL injection attacks (see Bobby Tables).

how to redirect the execution of a sql script (without the spool command) to an output file using a ksh script

I want to run this command from a KSH script, which allows to run the sql script (without using spool)
sqlplus -s $CONNEXION_BDD #${FILE_SQL} >> $OUTPUT_FILE
but it does not work, she starts without any results.
I just created a table called numbers and ran in some data:
create table numbers (num varchar(10));
insert into numbers values ('one');
insert into numbers values ('two');
insert into numbers values ('three');
insert into numbers values ('four');
commit;
And selected the data out using sqlplus:
$ cat get_num.sql
select * from numbers;
quit;
$ sqlplus -s $CONN_STR #get_num.sql > get_num.out
$ cat get_num.out
NUM
----------
one
two
three
four
Here is another version that uses the here-doc:
#!/bin/ksh
sqlplus -s $CONN_STR <<EOF > get_num.out
select * from numbers;
quit;
EOF
If you want to use PL/SQL:
#!/bin/ksh
sqlplus -s $CONN_STR <<EOF > tryCur.out
set serveroutput on size 30000;
DECLARE
thisNum varchar2(10);
CURSOR num_cur is
SELECT num FROM numbers;
BEGIN
OPEN num_cur;
LOOP
FETCH num_cur into thisNum;
EXIT WHEN num_cur%notfound;
dbms_output.put_line('ThisNum is:'|| thisNum);
END LOOP;
CLOSE num_cur;
END;
/
EOF
This version produced the output file:
ThisNum is:one
ThisNum is:two
ThisNum is:three
ThisNum is:four
PL/SQL procedure successfully completed.

Export results from DB2 to CSV including column names via bash

This question branches off a question already asked.
I want to make a csv file with the db2 results including column names.
EXPORT TO ...
SELECT 1 as id, 'COL1', 'COL2', 'COL3' FROM sysibm.sysdummy1
UNION ALL
(SELECT 2 as id, COL1, COL2, COL3 FROM myTable)
ORDER BY id
While this does work, I am left with an unwanted column and rows of 1 and 2's
Is there a way to do this via the db2 command or a full bash alternative without redundant columns while keeping the header at the top?
e.g.
Column 1 Column 2 Column 3
data 1 data 2 data3
... ... ...
instead of:
1 Column 1 Column 2 Column 3
2 data 1 data 2 data3
2 ... ... ...
All the answers I've seen use two separate export statements. The first generates the column headers:
db2 "EXPORT TO /tmp/header.csv of del
SELECT
SUBSTR(REPLACE(REPLACE(XMLSERIALIZE(CONTENT XMLAGG(XMLELEMENT(NAME c,colname)
ORDER BY colno) AS VARCHAR(1500)),'<C>',', '),'</C>',''),3)
FROM syscat.columns WHERE tabschema=${SCHEMA} and tabname=${TABLE}"
then the query body
db2 "EXPORT TO /tmp/body.csv of del
SELECT * FROM ${SCHEMA}.${TABLE}"
then
cat /tmp/header.csv /tmp/body.csv > ${TABLE}.csv
If you just want the headers for the extracted data and you want those headers to always be on top and you want to be able to change the names of those headers so it appears more user-friendly and put it all into a CSV file.
You can do the following:
# Creates headers and new output file
HEADERS="ID,USERNAME,EMAIL,ACCOUNT DISABLED?"
echo "$HEADERS" > "$OUTPUT_FILE"
# Gets results from database
db2 -x "select ID, USERNAME, DISABLED FROM ${SCHEMA}.USER WHERE lcase(EMAIL)=lcase('$USER_EMAIL')" | while read ID USERNAME DISABLED ;
do
# Appends result to file
echo "${ID},${USERNAME},${USER_EMAIL},${DISABLED}" >> "$OUTPUT_FILE"
done
No temporary files or merging required.
Db2 for Linux/Unix/Windows lacks a (long overdue) simple opting (to the export command) for this common requirement.
But using the bash shell you can run two separate exports (one for the column-headers, the other for the data ) and concat the results to a file via an intermediate named pipe.
Using an intermediate named pipe means you don't need two flat-file copies of the data.
It is ugly and awkward but it works.
Example fragment (you can initialize the variables to suit your environment):
mkfifo ${target_file_tmp}
(( $? != 0 )) && print "\nERROR: failed to create named pipe ${target_file_tmp}" && exit 1
db2 -v "EXPORT TO ${target_file_header} of del SELECT 'COL1', 'COL2', 'COL3' FROM sysibm.sysdummy1 "
cat ${target_file_header} ${target_file_tmp} >> ${target_file} &
(( $? > 0 )) && print "Failed to append ${target_file} . Check permissions and free space" && exit 1
db2 -v "EXPORT TO ${target_file_tmp} of del SELECT COL1, COL2, COL3 FROM myTable ORDER BY 1 "
rc=$?
(( rc == 1 )) && print "Export found no rows matching the query" && exit 1
(( rc == 2 )) && print "Export completed with warnings, your data might not be what you expect" && exit 1
(( rc > 2 )) && print "Export failed. Check the messages from export" && exit 1
This would work for your simple case
EXPORT TO ...
SELECT C1, C2, C3 FROM (
SELECT 1 as id, 'COL1' as C1, 'COL2' as C2, 'COL3' as C3 FROM sysibm.sysdummy1
UNION ALL
(SELECT 2 as id, COL1, COL2, COL3 FROM myTable)
)
ORDER BY id
Longer term, EXTERNAL TABLE support (already in Db2 Warehouse) which has the INCLUDEHEADER option is (I guess) going to appear in Db2 at some point.
I wrote a stored procedure that extracts the header via describe command. The names can be retrieved from a temporary table, and be exported to a file. The only thing it is still not possible is to concatenate the files via SQL, thus a cat to both file and redirection to another file is necessary as last step.
CALL DBA.GENERATE_HEADERS('SELECT * FORM SYSCAT.TABLES') #
EXPORT TO myfile_header OF DEL SELECT * FROM SESSION.header #
EXPORT TO myfile_body OF DEL SELECT * FORM SYSCAT.TABLES #
!cat myfile_header myfile_body > myfile #
The code of the stored procedure is at: https://gist.github.com/angoca/8a2d616cd1159e5d59eff7b82f672b72
More information at: https://angocadb2.blogspot.com/2019/11/export-headers-of-export-in-db2.html.

Assign query result to variable and access it from other file

I have two files namely file1.sh and file2.sh.
The file1.sh contains the DB2 query, the query return the total number of employees in the employee table.
Now I want to assign the total number of employees into a variable within the file file1.sh.
File 1:
#!/bin/bash
#database connection goes here
echo The total number employees:
db2 -x "select count(*) from employee"
When i run above file that display the total number of employees.
But
I want to store that total into some variable and want it to access from another file that is file2.sh.
File 2:
#!/bin/bash
#Here i want to use total number of employees
#Variable to be accessed here
Using the following two scripts, driver.sh and child.sh:
driver.sh
#!/bin/bash
cnt=`./child.sh syscat.tables`
echo "Number of tables: ${RESULT}"
cnt=`./child.sh syscat.columns`
echo "Number of columns: ${RESULT}"
child.sh
#!/bin/bash
db2 connect to pocdb > /dev/null 2>&1
cnt=`db2 -x "select count(*) from ${1}"`
db2 connect reset > /dev/null 2>&1
db2 terminate > /dev/null 2>&1
echo ${cnt}
results
[db2inst1#dbms stack]$ ./driver.sh
Number of tables: 474
Number of columns: 7006
[db2inst1#dbms stack]$ ./child.sh syscat.columns
7006

Passing IFS values to a dynamic array

i have a Oracle output (select output) as cand1 cand2 cand3 cand62
now i need to store these values in an array through shell script.
#!/bin/bash
instant_client="/root/ora_client/instantclient_11_2"
output=`$instant_client/sqlplus -s HRUSER/HRUSER#TOMLWF <<EOF
set heading off
set feedback off
set lines 10000
set pagesize 10000
select count (1) from onboardingcandidates o, candidatedetails c where o.candidateid=c.candidateid and o.JOININGSTATUS='0091' and to_date(o.joiningdate)=to_date(sysdate+5);
EOF
exit
query=`$instant_client/sqlplus -s HRUSER/HRUSER#TOMLWF <<EOF
set heading off
set feedback off
set lines 10000
set pagesize 10000
select o.candidateid from onboardingcandidates o, candidatedetails c where o.candidateid=c.candidateid and o.JOININGSTATUS='0091' and to_date(o.joiningdate)=to_date(sysdate+5);
EOF
exit
i=0
echo "Throwing individual arrays:"
while [ $i -le $output ]
do
a=${query[$i]}
echo Candidate[$i]=$a
i=$(($i+1))
done
OUTPUT IS
Throwing individual arrays:
Candidate[0]= cand1 cand2 cand3 cand62
Candidate[1]=
Candidate[2]=
Candidate[3]=
Candidate[4]=
REQUIRED OUTPUT
Throwing individual arrays:
Candidate[0]= cand1
Candidate[1]= cand2
Candidate[2]= cand3
Candidate[3]= cand62
The problem is that query is a string, not an array, so ${query[$i]} does not work as you expect.
In general, to convert a string to an array you would do this:
$ string="foo bar"
$ array=($string)
$ echo ${array[0]}
foo
$ echo ${array[1]}
bar
In your case, if you surround the sqlplus command with parentheses, the output will be stored in an array. Like this:
query=(`$instant_client/sqlplus -s HRUSER/HRUSER#TOMLWF <<EOF
set heading off
set feedback off
set lines 10000
set pagesize 10000
select o.candidateid from onboardingcandidates o, candidatedetails c where o.candidateid=c.candidateid and o.JOININGSTATUS='0091' and to_date(o.joiningdate)=to_date(sysdate+5);
EOF)
And then you can access elements in the query array using an index: ${query[$i]}
Something like this:
$ query="cand1 cand2 cand3 cand62"
$ read -a Candidate <<<$query
$ echo ${Candidate[0]}
cand1
$ echo ${Candidate[1]}
cand2
When you are out of your sqlplus, your "query" variable contains something like above. By simply reading the entire thing into an array as shown above, you can access the values.

Resources