Oracle case running all prompts - oracle

I've made this program and I was wondering why oracle prompts for input even when it does not run the 'D' case (when 'A' is imputed for example).
SET SERVEROUTPUT ON;
ACCEPT p_user_letter PROMPT 'Enter an option';
DECLARE
v_user_letter VARCHAR2(200) := UPPER('&p_user_letter');
BEGIN
CASE v_user_letter
when 'A' then dbms_output.put_line('A pressed');
when 'D' then new_customer('&userEntered');
else dbms_output.put_line('Other');
END CASE;
END;
/
CREATE OR REPLACE PROCEDURE new_customer
(ccName IN Varchar2)
IS
BEGIN
dbms_output.put_line('Ran procedure');
END new_customer;
From the error message it seems like it runs a new and old version of declare which forces all uninitialized prompts to have a value, but i'm not certain and could not find out why online.
Both new and old running

('&userEntered'); is a substitution variable.
The substitution variables are not a part of SQL, this is a feature of SQL-Plus client, it is also supported by SQL-Developer.
When you hit Enter (or Run icon in SQL-Developer), SQL-Plus first analyses your script. When it finds &xxx string in your script, then prompts the user for a value. When the user enters the value then SQL-Plus substitutes (replaces) &xxx with the value entered by the user.
When SQL-Plus substitutes all substitution variables, then it starts to execute this script - that is, it sends SQL commands from the script to the Oracle Database for execution.
You can think of the variable substitution in SQL-Plus as a kind of macro preprocesing.

Related

Unable to get correct result from simple query in cmd PL/SQL

I was trying to print "Hello World", but I continue to get number 7 returned. Also, after the 7 is returned, I cannot run another file without restarting cmd window. If I hit enter key, the number increments by 1. Please explain why this is happening and possible ways to correct.
Put a / on a new line at the end of the file to terminate the PL/SQL anonymous block.
SET SERVEROUTPUT ON;
DECLARE
name VARCHAR2(20);
BEGIN
name := 'Hello World';
DBMS_OUTPUT.PUT_LINE(name);
END;
/
SQL/Plus has read your file but has not found the statement terminator for the PL/SQL block so it is continuing to ask for more input to complete the statement. It is expecting a / character on a new line (the 7th line of the statement, the first being DECLARE as the SET command is a different statement) and when you press the enter key then it moves on to line 8 of the statement and is still expecting the terminator character so asks for more input (and repeats for the 9th, 10th and 11th lines).
You could press / then enter in SQL/Plus but it would be better to fix the file so it contains complete statements.

Using Variable- Oracle

In the above code, I am giving schemaname as input and using that input it should connect to the database. But In this case the value i entered is not taken by the schemaname. This is how the out put and the error is:
declare schemaname varchar2(20);
exec :schemaname := XYZ;
BEGIN
end;
Error report -
ORA-06550: line 2, column 6:
PLS-00103: Encountered the symbol "" when expecting one of the following:
constant exception <an identifier>
<a double-quoted delimited-identifier> table long double ref
char time timestamp interval date binary national character
nchar
ORA-06550: line 4, column 1:
PLS-00103: Encountered the symbol "CONNECT" when expecting one of the following:
Could any one suggest how to make it work using spool
the code between declare and end is PL/SQL. Commands like CONNECT or SPOOL are SQL*Plus commands. You cannot use SQL*Plus commands in a PL/SQL block.
In your case you don't need PL/SQL at all:
Create a script with following content
connect &1
spool C:\ABC
#c:\ABC
spool off;
and run it
#your_script_name
BTW: there is no reason to run script c:\ABC while you are spooling into it. What exactly do you want to achieve?
exec[ute] is SQL*Plus and SQL Developer (and maybe other clients) shorthand for an anonymous block. It is a client command, it is not part of PL/SQL. You are trying to use it inside a PL/SQL declare section, where it is not valid or recognised.
If you want a client bind variable you need the var[iable] command:
var schemaname varchar2(20);
exec :schemaname := '&1';
BEGIN
...
Notice the single quotes around &1, as it's being assigned to a string variable.
But you can't connect inside a PL/SQL block either, and you can't use a bind variable for the connection.
connect :schemaname
will prompt for a password (even if you defined it's value as user/passwd) and try to connect as a user lieterally called :schemaname.
You can use a substituion variable, but you don't really need to define a new one; as you seem to be passing the credentials in, you can do:
connect &1
(without surrounding quotes)

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

Passing Contents of File as Parameter to Sql*Plus Command

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.

How do I exit a script in SQLPlus when an error occurs and return to the SQLPlus prompt, without disconnecting or exiting SQLPlus?

I have some scripts that get run often, always from within a connected SQLPlus session.
I need a way to exit the script when an error occurs, without disconnecting or exiting SQLPlus itself. 100% of the time, when an error occurs, the connected DBA will need to issue one or more commands into the session. 100% of the time, when an error occurs, there are other SQLPlus statements (and thus must be outside of a BEGIN..END;) later on in the script that must not be executed or serious problems could arise.
NOTE: If you suggest WHENEVER SQLERROR EXIT then you didn't read the above text. That will disconnect and exit SQLPlus in addition to the script, which is not acceptable behavior.
I've found an interesting idea here which, when combined with spencer7593's answer, will get me selective sub-script calling, to which I can pass the PL/SQL output values. To wit:
VAR continue number;
EXEC :continue := 1;
BEGIN
SELECT some_bool_test() INTO :continue FROM dual;
END;
SET termout OFF
COLUMN script_name NEW_VALUE v_script_name
SELECT decode(:continue, 1, 'run_stuff.sql', 'skip.sql') script_name FROM dual;
SET termout ON
#&v_script_name :some_other_values
Where skip.sql is an empty text file.
UPDATE: I've moved most of this into a RUN.SQL file, where I pass in the boolean (0 or 1) as &1, the script name to call on success as &2, and then any other expected parameters to pass to the called script. Thus, it ends up looking something like this:
VAR continue number;
EXEC :continue := 1;
BEGIN
SELECT some_bool_test() INTO :continue FROM dual;
END;
#run.sql :continue 'run_stuff.sql' :some_other_values
It's not possible.
SQLPlus doesn't provide that level of control over the execution of a script.
Obviously, you'd need to AVOID using the WHENEVER SQLERROR EXIT ... command.
It's possible to gain conditional control over which SQL statements do or do not get executed as a result of raised exceptions (errors) using PL/SQL. But that doesn't address SQLPlus commands (which cannot be executed from within a PL/SQL block.)
DECLARE
lb_continue BOOLEAN;
BEGIN
lb_continue := TRUE;
BEGIN
sql statement
EXCEPTION
WHEN OTHERS THEN
lb_continue = FALSE;
END;
IF lb_continue THEN
BEGIN
sql statements
EXCEPTION
WHEN OTHERS THEN
lb_continue := FALSE;
END;
END;
Of course, that approach has it's own limitations and issues. Any DDL statements would need to be called dynamically; the easiest way to do that is an EXECUTE IMMEDIATE statement.
The biggest issue (in your case) is that it's not possible to execute SQLPlus commands from inside a PL/SQL block.
You can't exit the script and stay in SQL*Plus, but you can stop executing things. It isn't pretty, but assuming you can modify the script to add the control flow then you can just about do this with bind variable.
set serveroutput on
var flag char;
exec :flag := 'Y';
begin
if :flag != 'Y' then
raise program_error;
end if;
dbms_output.put_line('Doing some work');
/* Check for some error condition */
if 0 != 1 then
raise program_error;
end if;
/* Only reach this if earlier statements didn't fail
* but could wrap in another flag check if needed */
dbms_output.put_line('Doing some more work');
exception
when program_error then
dbms_output.put_line(sqlerrm);
:flag := 'N';
when others then
/* Real exception handling, obviously */
dbms_output.put_line(sqlerrm);
:flag := 'N';
end;
/
-- DML only does anything if flag stayed Y
select sysdate from dual
where :flag = 'Y';
-- Optional status message at the end of the script, for DBA info
set feedback off
set head off
select 'Something went wrong' from dual where :flag != 'Y';
set feedback on
set head on
When executed:
SQL> #script
PL/SQL procedure successfully completed.
Doing some work
ORA-06501: PL/SQL: program error
PL/SQL procedure successfully completed.
no rows selected
Something went wrong
SQL>
Any PL/SQL blocks in the script can check the flag status at the start, and raise program_error (just as a handy pre-defined exception) to jump back out. Anything that errors inside a PL/SQL block can update the bind variable flag, either directly or in an exception handler. And any non-PL/SQL DML can have an additional where clause to check the flag status, so if it's been set to N by the time that statement is reached, no work is done. (For an insert I guess that would mean not using the values form).
What this can't do is deal with any errors from plain SQL statements, but I'm not sure if that's an issue. If it is then those might need to be changed to dynamic SQL inside a PL/SQL block.
I know its old, but these two instructions at the very begining of the SQL script do the work:
WHENEVER SQLERROR EXIT FAILURE ROLLBACK
WHENEVER OSERROR EXIT FAILURE ROLLBACK

Resources