Passing Contents of File as Parameter to Sql*Plus Command - oracle

I'm trying to write a sqlplus command that creates a table from a query that is stored in an .sql file.
The particular .sql file that contains the query would be supplied to my sqlplus command as a variable (&v_InputQuery).
I've tried something like this, but it doesn't work.
CREATE TABLE &v_OutputTable AS
(
< &v_InputQuery
)
;
I get an error saying that there's a missing SELECT keyword.
What I'd really like is for &v_InputQuery to be replaced not with the name of the file specified by the user, but with the actual contents of the file. Is there a way to do that?
Thank you very much.

Yes, you can do that. If your query is in a file called v_InputQuery.sql, you can do this:
CREATE TABLE &v_OutputTable AS (
#v_InputQuery.sql
) ;
It's important that the # is the first character on the line. SQL*Plus will read the file and put its contents at that location. So make sure you don't have any terminating characters in the file such as ; or /.

Unfortunately, You cannot create a SQL*Plus command, but instead create a shell script to do it!
Lets say my_script.sh is below
#you can always complete the user interaction at unix/dos
USER=your_user
PASS=your_pass
DB=your_db
OUTPUT_TABLE=$1;
QUERY_FILE=$2;
SELECT_QUERY=`cat $QUERY_FILE`;
sqlplus -S ${USER}/${PASS}#${DB} << !
SET SERVEROUTPUT ON;
VAR EXITCODE NUMBER;
BEGIN
EXECUTE IMMEDIATE ' CREATE TABLE $OUTPUT_TABLE AS $SELECT_QUERY ';
:EXITCODE := SQLCODE;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
:EXITCODE := SQLCODE;
END;
/
exit :EXITCODE
!
Executing the script as below(Depends on OS)
ksh my_script MY_OUTPUT_TABLE my_sql.sql;

Expanding a comment, #MaheswaranRavisankar's approach will work, but the dynamic SQL (i.e. execute immediate) isn't necessary, so the anonymous block isn't necessary either. It can be simplified somewhat to:
USER=your_user
PASS=your_pass
DB=your_db
OUTPUT_TABLE=$1;
QUERY_FILE=$2;
SELECT_QUERY=`cat $QUERY_FILE`;
sqlplus -S ${USER}/${PASS}#${DB} << !
WHENEVER SQLERROR EXIT FAILURE
CREATE TABLE $OUTPUT_TABLE AS $SELECT_QUERY
!
This also allows you to use a query which is already terminated by a ; or /, which the execute immediate version wouldn't like - you just need to decide whether your wrapper script needs one to match what your query files will contain.
Even the whenever ... line isn't vital, but the other answer tried to exit with the error code so I've mimicked that too somewhat. This will always exit with a generic failure status though (1 in Unix, not sure what Windows does). You can then test if it was successful with $? in the script if you want to.
You can exit with the actual SQL error instead of the generic value, by using whenever sqlerror exit sql.sqlcode instead. The problem with doing that is that most (all?) shells limit the return code to the range 0-255, so most errors will wrap and give something unhelpful anyway - a fairly-likely ORA-00955: name is already used by an existing object error would give a shell exit value of 187, for example. And it's possible the wrapped value would be zero, which would mask that an error occurred at all; an also-plausible ORA-01536: space quota exceeded for tablespace '%s' error would give a shell exit code of zero, which is unhelpful. Using exit failure would at least stop that.

Related

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.

How to execute a oracle procedure having out parameter inside a unix script

Can any one please tell me how to execute a simple oracle stored procedure having out parameter inside a shell script.That means it should return a value into the unix environment.
I assume you want to start a script using SQLPLUS. This answer explains how to assign the value of an out parameter to a bind variable in SQLPLUS.
Call stored procedure from sqlplus
You can exit sqlplus with this value and use that value in the calling script.
exit x
But this usually is restricted to numerical values in a limited range.
There are a number of ways, but the one I tend to use is illustrated below.
The sqlplus script the_sql_script.sql
var ret varchar2(2000)
exec the_procedure ( the_out_param => :ret );
set pages 0 head off lines 200 trimspool on
spool sqlplus.return
select 'RETURN_VALUE=' || :ret FROM dual;
spool off
quit
In shell:
sqlplus / # the_sql_script.sql
. ./sqlplus.return
echo $RETURN_VALUE

how to write a batch file, which runs a PL/SQL block, to tell me many records are inserted/deleted/etc. in a log file?

I have created two batch files to run two separate .sql file in Windows Task Scheduler. The batch file for both looks like this:
sqlplus userid/password#database #C:\XXX.sql>>C:\output.log
echo commit; | userid/password#database
The first .sql file (SQL1) is a PL/SQL block like this:
SET SERVEROUT ON
DECLARE
....
BEGIN
IF ...
....
ELSE
#D:\DM_FIX.sql;
END IF
END
The DM_FIX.sql file is to insert a bunch of records into a table, and it starts with the INSERT command.
The second.sql file is not a block file. It's doing a bunch of DDL/DML comand. the file looks like below:
Truncate Table YYY
Reuse Storage;
Commit;
Insert into Table YYY
Select ... from
Commit;
Delete from Table YYY
where ...
Commit;
When I run the second .sql file, I get an output that indicates "The table has been truncated; #### records are inserted; #### records are deleted..."
But when I run the first, although the PL/SQL procedure is executed successfully, I don't get a line saying how many records are inserted, and I'm trying to figure out a way to do it.
Does any one know what could be the trick?
Thanks!
Revised Answer
As #Alex Poole helpfully pointed out, you can use the # nomenclature in a PL/SQL block from SQL*Plus, as that would load the second file's commands into the block that you're calling it from.
The reason that you're not getting any output is that, as far as SQL*Plus is concerned it's all one command: the SQLPlus block. In order to get output to your log for those commands, you'll need to create it yourself, using DBMS_OUTPUT. You would need to include a line like the one below after each command.
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' rows inserted');
However, you should note that doing this will cause your script to throw errors if it's ever called from outside of a PL/SQL block. My original solution would avoid this restriction, as it eliminates the need to use DBMS_OUTPUT.
Original Answer
I find it difficult to believe that the PL/SQL in the first file is executing successfully. This is because you're mixing SQL*Plus commands with PL/SQL code. They are separate systems - it's like trying to use shell commands natively in a programming language. You should be getting PLS-00103: Encountered the symbol "#" when expecting... from the first file.
SQL*Plus doesn't have conditionals, so, in order to keep the PL/SQL and SQLPlus commands seperate, you'd need to fake it somewhat. I'd suggest putting the file name into a substitution variable, then using that to run a file:
VARIABLE v_my_file_bind varchar2(100)
DECLARE
...
BEGIN
IF ...
...
:v_my_file_bind := 'D:\EMPTY_FILE.sql';
ELSE
:v_my_file_bind := 'D:\DM_FIX.sql';
END IF;
END;
/
COLUMN v_my_file_column new_value my_file_substitution noprint
SELECT :v_my_file_bind v_my_file_column from dual;
#&&my_file_substitution
To show count of modified rows in PL/SQL, use SQL%ROWCOUNT:
begin
insert into my_table ...
select ...
from ...;
dbms_output.put_line('Rows inserted: ' || SQL%ROWCOUNT);
commit;
end;
/
Remember, that SQL%ROWCOUNT variable will reset to 0 after commit.
Documentation: http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/sql_cursor.htm

"unknown command" for dbms_lob.open command in Sql+

I am a total beginner when it comes to PL/SQL and Sql+. I am trying to write a function that will extract the contents of a text file into a CLOB (following this as an example). When I issue the following command in Sql+:
dbms_lob.open( 'c:\temp\test.txt', dbms_lob.lob_readonly );
I get the following error message:
SP2-0734: unknown command beginning "dbms_lob.o..." - rest of line ignored.
Is there something wrong with the syntax of the command, or something else entirely?
Thanks much!
I'm not an expert in oracle. For me the problem is that for launch this command in sqlplus you may declare an anonymous block like this:
DECLARE
-- variables
BEGIN
--- your commands here
dbms_lob.open......
END;
To launch the execution you have to digit / and then return

Can Sqlplus read the contents of a file into a variable?

I've been tinkering with sqlplus for awhile now, and I've managed to get sqlplus to read the contents of a file into a substitution variable as such:
exit | sqlplus -s login/pass#db #script.sql "`cat file.txt`"
This mostly works as my script requires... even newlines in the file are preserved. However, I was only using a sample file which was 50 or 60 bytes in size. The actual files I'll end up using will be at least a few kilobytes. So it was at this point I decided to check the max size of a substitution variable: 240 characters.
Is there a way within my sqlplus script to read a file's contents into a bind variable? The Oracle documentation seems to hint at this with the GET command, saying that typically you'll use this just to load a sql/sqlplus script.
http://docs.oracle.com/cd/B10501_01/server.920/a90842/ch13.htm#1009882
file_name[.ext] Represents the file you wish to load (typically a script).
Yes, there's a tricky way to do it.
Put something into props.txt and run the script:
DECLARE
-- the ## expression must be in separate line as follows
file_contents VARCHAR2(32767) := '
##props.txt
';
BEGIN
dbms_output.put_line('===');
dbms_output.put_line(file_contents);
dbms_output.put_line('===');
END;
/
Note that the file props.txt can not contain an "#" or you'll get nested
SQL*PLUS calls
No. Load would only store the file contents in Sql*Plus's own sql buffer. You can then run, edit and list the buffer.
A substitution variable is not the right place to load a file into. Use a bind variable of type clob for that and load the file contents using utl_file. But of course the file has to be located on the server in this case.
edit: if the data has to be located on the client, your option would be to load the clob using a pl/sql block and several calls to dbms_lob.writeappend
Your file would have to look like this (cannot test it ATM):
var l clob;
begin
dbms_lob.createtemporary(l);
dbms_lob.writeappend(l, 'abcdef...');
dbms_lob.writeappend(l, 'ijkl...');
end;
/
Please consider using literal quoted string - this allows you to have quotes in the linked file:
DECLARE
-- the ## expression must be in separate line as follows
file_contents VARCHAR2(32767) := q'[
##props.txt
]';
BEGIN
dbms_output.put_line('===');
dbms_output.put_line(file_contents);
dbms_output.put_line('===');
END;
/

Resources