Parameterizing table name in sqlplus input file - oracle

I am trying to export some data using sqlplus and the Oracle spool functionality. The problem is that the SQL input file where I am defining my export is not letting me parameterize the table name from which I am exporting data -- it wants a literal :(
Calling sqlplus in a shell script:
sqlplus $USER/$PASSWD#$ORADB<<!
#export.sql $OUT_FILE $SOME_VAR $ENV
exit
!
export.sql:
set heading off
set head off
set term off
set tab off
set embedded on
set feedback off
set pagesize 0
set linesize 800
set trimspool on
set verify off
spool &1
SELECT '&2|' || some_col
FROM &3_TABLE
/
spool off
When $ENV is set to 'dev', I get
Enter value for 3_TABLE
whereas I want it to use dev_TABLE. When I unparameterize the table names in the sql file, the output runs fine. Also note that there is param &2, which is $SOME_VAR from the shell and it gets displayed evaluated fine. The only problem is in the FROM statement.
Is there any way to tell the sql input file to replace the parameterized table names before running SQL?
Thanks

The problem is that SQL*Plus is treating the whole string after the &, up to the next whitespace or simlar, as the substitution variable name. Clearly that isn't what you want here.
Fortunately they've thought of this, and you can denote the end of the variable name with a .:
FROM &3._TABLE
(At least, that works for named variables, and I'm almost sure it will for positional ones... if not then you'd need to define a new variable set to &3 as a workaround).
It is in the documentation, but blink and you'll miss it:
If you wish to append characters immediately after a substitution
variable, use a period to separate the variable from the character.
There's a related effect that you may want to bear in mind for the future. If the next character after the substitution variable is a . anyway - between the schema and table, or between table and column, for example - then that will be interpreted as the substitution terminator. Say you were passing the schema separately as &4, with value 'scott'; this:
FROM &4.&3._TABLE
looks reasonable but would be substituted as scottdev_TABLE, which won't be recognised. So in that instance you need to have an extra one:
FROM &4..&3._TABLE
which would be substituted as scott.dev_TABLE.

Related

keeping & ampersand as a value in a sql where condition with other substitute variable values [duplicate]

I have a SQL script that creates a package with a comment containing an ampersand (&). When I run the script from SQL Plus, I am prompted to enter a substitute value for the string starting with &. How do I disable this feature so that SQL Plus ignores the ampersand?
This may work for you:
set define off
Otherwise the ampersand needs to be at the end of a string,
'StackOverflow &' || ' you'
EDIT: I was click-happy when saving... This was referenced from a blog.
If you sometimes use substitution variables you might not want to turn define off. In these cases you could convert the ampersand from its numeric equivalent as in || Chr(38) || or append it as a single character as in || '&' ||.
I resolved with the code below:
set escape on
and put a \ beside & in the left 'value_\&_intert'
Att
You can set the special character, which is looked for upon execution of a script, to another value by means of using the SET DEFINE <1_CHARACTER>
By default, the DEFINE function itself is on, and it is set to &
It can be turned off - as mentioned already - but it can be avoided as well by means of setting it to a different value. Be very aware of what sign you set it to. In the below example, I've chose the # character, but that choice is just an example.
SQL> select '&var_ampersand #var_hash' from dual;
Enter value for var_ampersand: a value
'AVALUE#VAR_HASH'
-----------------
a value #var_hash
SQL> set define #
SQL> r
1* select '&var_ampersand #var_hash' from dual
Enter value for var_hash: another value
'&VAR_AMPERSANDANOTHERVALUE'
----------------------------
&var_ampersand another value
SQL>
set define off <- This is the best solution I found
I also tried...
set define }
I was able to insert several records containing ampersand characters '&' but I cannot use the '}' character into the text
So I decided to use "set define off" and everything works as it should.
According to this nice FAQ there are a couple solutions.
You might also be able to escape the ampersand with the backslash character \ if you can modify the comment.
I had a CASE statement with WHEN column = 'sometext & more text' THEN ....
I replaced it with
WHEN column = 'sometext ' || CHR(38) || ' more text' THEN ...
you could also use
WHEN column LIKE 'sometext _ more text' THEN ...
(_ is the wildcard for a single character)

Put select result in a ksh variable

using sql loader, I know I can reference a ksh variable in my ctl file. For example I can write
LOAD DATA
INFILE '$PATH_IN_KSH/my_file.dat'
...
I would like to add a WHEN clause like this
WHEN (125:125) = '$P_NUMBER'
P_NUMBER would have the value of a column in a table that I would retrieve with a select query.
Is it possible to do that ? retrieve a value from a column with a select and somehow put it in the ksh variable so the ctl file can see it. (something with sql plus?)
Thank you
As a basic outline you can run SQL*Plus with a heredoc to perform the query, and assign the output to a variable:
P_NUMBER=`sqlplus -s /nolog <<!EOF
connect username/password
whenever sqlerror exit failure
set pagesize 0
set feedback off
select your_value from your_table where your_key = 'something';
exit 0
!EOF`
Enclosing in backticks assigns the result to the variable. $P_NUMBER will then hold whatever value your query got (or an error message if the credentials were wrong, say). It helps if you're sure the query will return exactly one result. You can also test the return code with $? to look for errors, before you try to use your variable.
Including the -s flag, turning off feedback and setting the pagesize to zero collectively suppress all the noise so you only get the result and don't have to strip out banners, headings etc.
And finally I've used /nolog and put the connect statement inside the heredoc so that the credentials don't appear in the process list, which is an often-overlooked security issue. If you don't want to do that and do put the credentials as sqlplus username/passwd, you can add the -l flag so that it only tries to log in once; otherwise if login fails for some reason it'll try to use the rest of the heredoc as further credentials, and can appear to get hung up with short scripts.

Need to pass string with newline character from Unix shell script to sql query

I am using unix shell KSH scripting to do some table cleanup.
I have a file "partner.txt" with 5000 line like this
>cat partner.txt
aaa0000
aaa0001
aaa0002
...
...
aaa5000
Using this file, I am supposed to clean few tables with matching, say agreements of the partners.
So i am constructing a partner list string in the format that i can use in the sql statement with 'IN' clause (select * from tab where partner IN partner_list)
('aaa0000',
'aaa0001','aaa0002',...,'aaa0010',
'aaa0011','aaa0012',...,'aaa0020',
...
'aaa4990','aaa4991',...,'aaa5000')
I am assigning the string to partner_list variable like this.
export BO="("
export BC=")"
export BQ="('"
export QC="','"
export QB="')"
export C=","
export CE=","'\n'
export QCE="',"'\n'"'"
partnerListLine=${BO}
while read partnerline;
do
if [ `expr ${counter} % 10` -eq 0 ]
then
partnerListLine=${partnerListLine}${partnerline}${CE}
elif [ ${counter} -lt ${numOfObsoletePartner} ]
then
partnerListLine=${partnerListLine}${partnerline}${C}
fi
counter=`expr ${counter} + 1`
done < partner.txt
partnerListLine=${partnerListLine}${partnerline}${BC}
Then I am using this partner list to fetch my agreement list like
SQL_agreement='select distinct a.agreement from partner_agreement_map a where a.partner in ${partner_list} order by agreement asc;'
I needed the newline character in my partner list since i was using sqlplus and was encountering SP2-0027: Input is too long (> 2499 characters)
I am adding the newline character by appending the below to my partner list string after N partners
CE=","'\n'
This worked fine when i was using sqlplus directly in the script.
But when i try to pass this partner_list string as parameter to a sql script, it shows '\n' in the query.
This is how i call my sql script and pass the parameter
sqlplus -s ${REFERENCE_DB_USER}/${REFERENCE_DB_PASS}#${DATABASE_INSTANCE} << !!
set serveroutput on size 10000;
set feedback off;
set verify off;
set echo off;
set term off;
set pagesize 0;
SET linesize 1000;
SET TRIMSPOOL ON;
spool 1_del_agreement_spool_$$.lst;
#1_del_agreement.sql ${partner_list};
spool off;
exit;
/
!!
this is my spooled file
>cat 1_del_agreement_spool_18165.lst <
select distinct a.agreement from partner_agreement_map a where a.partner in ('aaa0000',\n'aaa0001','aaa0002','aaa0003',...'aaa0010',\n'aaa0011'...) order by agreement asc
*
ERROR at line 1:
ORA-00907: missing right parenthesis
How can i maintain the newline character when i pass the parameter to the sql script and not have it replaced to '\n'?
I have tried ANSI-C quoting but failed.
Please let me know if you would need more details of the shell or sql script
UPDATED MY ENTIRE SOLUTION DESIGN
After trying all night, i have given up.
Thanks Aaron and mplf for your inputs.
I have decided to change my solution from file based to table based. I will be reading the partner.txt file and inserting the partners in a dummy temporary table. Then I can formulate queries with ease on other tables.
In fact, i think this should have been my first design :) There may be something very minor that i was missing in the previous design. But anyways, this will be much easier
I wish my team lead ever reviews design rather than code formatting issues :P
If you are using bash you can use $'\n' to print a newline character which would make your example
if [ `expr ${counter} % 10` -eq 0 ]
then
partnerListLine=${partnerListLine}${partnerline}"',"$'\n'"'"
else
...
Example:
$ echo hello"',"$'\n'"'"
hello',
'
I don't have a working solution but a hint: '\n' means "insert the literal backslash followed by n". So you tell the shell to leave this string alone.
Try NL=$(echo -e '\n') or similar to get a string variable which actually contains a newline. Then you can define CE=",$NL"
The shell might preserve this new line character as it processes the string.
Or use a tool like awk to create a string value with newlines which you assign to partner_list with partner_list=$(awk ...) to prevent the shell from doing any kind of processing of the value.
If that doesn't work, you may have to write the data to a file (with new lines).

How to create a "one-liner" for oracle that includes "set" commands as well as sql statements

I want to execute a dynamic sql containing some set commands. Is it possible to do so without embedding newlines?
set heading off ; set lines 1000 ; select * from my_table;
Note the above does not work due to the semicolons between the set commands:
SP2-0158: unknown SET option ";"'
Update The whole point of this question is to do it on one line.
The best I have found for my own purposes is to put my standard SET commands in a file called sql_settings.txt in a directory with an environment variable holding its path and another variable for the connect string:
sqlsets=/directory/where/sql_settings/stored/sql_settings.txt
db_conn=<ConnectStr>
& then execute a one-liner as such with a shell here-string:
sqlplus -s $db_conn #$sqlsets <<< "select * from my_table;" | less
(The "less" pipe will prevent from cluttering your shell session)
You could also get fancy and create a shell function to minimize typing to the SQL query:
function mydb { sqlplus -s $db_conn #$sqlsets <<< "$#;" ; }
Then call as such:
mydb 'select * from my_table;'
set command is a directive for sqlplus and is not related to sql and you can do it this way
set heading off lines 1000
select * from my_table;
After extensive research, I have concluded this is not possible to perform with oracle.

Config SQL*Plus to return nothing but data

I need to write a simple shell function that returns a single field from an Oracle DB. Think of it as for example SELECT 'ABC' FROM dual; and ABC is what I am after.
Here is my function:
function getSomeOraVal
{
sqlplus $USER/$PASSWD#$ORADB<<!!
SET sqlprompt ''
SET sqlnumber off
SET verify off
SET pages 0
SET echo off
SET head on
SET feedback off
SET feed off
SET serveroutput on
SET escape '\'
VARIABLE v_someVal VARCHAR2(30);
BEGIN
SELECT 'ABC' INTO v_someVal FROM dual;
END;
/
SELECT :v_someVal FROM dual;
!!
}
However, I want to pipe the sqlplus output (data only -> 'ABC') into a shell variable, which the function then returns and can be called from other shell scripts. Unfortunately, sqlplus returns a whole lot of garbage, such as "Welcome to Oracle" on top and "Disconnected..." in the bottom.
How can I extract just the data from a SQL result set, or in this case a single value and pass it into a UNIX variable for further manipulation within the shell?
Thanks
There are a few different approaches in this askTom thread on returning values from SQL*Plus to a shell script.
One common approach is to select a constant token in addition to the value that you want to return (in Tom's example, that is the string "KEEP") and then use sed (or your favorite command-line parser) to extract the data you're actually interested in
#!/bin/ksh
x=`sqlplus / <<endl | grep KEEP | sed 's/KEEP//;s/[ ]//g'
select 'KEEP' , max(sal) from emp;
exit
endl`
echo the answer is $x
Other approaches, such as approaches that allow you to read multiple lines of output are also discussed in that thread.
If you don't want the header to be printed, you should be specifying
set head off
in your SQL*Plus script-- I'm not sure why you're explicitly setting the header on in the script if you don't want the header... Do you want to keep some part of the header?
Based on Justin's answer (which is great), when you only need to select one number (or token), I consider this a little shorter, yet more readable version:
x=`sqlplus -S / <<< "select 'KEEP' , max(sal) from emp;" | awk '/KEEP/{print $2}'`

Resources