Oracle Sqlplus calling sql script repeatedly in a loop - oracle

Is it possible to call a script inside a loop via sqlplus?
Suppose we have two files simple_sample.sql and main.sql:
|simple_sample.sql|
PROMPT HELLO &1
-------------------
|main.sql|
begin
for loop_parameter in 1..3 loop
#magic_call# #simple_sample.sql loop_parameter
end loop;
end;
/
What can be used instead of #magic_call# to obtain this output:
sqlplus user/password#schema #main.sql
HELLO 1
HELLO 2
HELLO 3

===PL/SQL===
1.1
You can call another script from PL/SQL but it will be inlined in the code and must be correct PL/SQL snippet.
So if we modify original scripts
simple_sample1.sql
dbms_output.put_line(&1);
main.sql
begin
for loop_parameter in 1..3 loop
#simple_sample1.sql loop_parameter
end loop;
end;
/
Then result is following
SQL> #main
old 3: dbms_output.put_line(&1);
new 3: dbms_output.put_line(loop_parameter);
1
2
3
PL/SQL procedure successfully completed.
Of course, inlined code may look almost like standalone block if you wrap it with "begin" and "end".
begin
dbms_output.put_line(&1);
end;
1.2 If you want to run arbitrary script from PL/SQL you can run sqlplus from PL/SQL code using DBMS_SCHEDULER... but it's a bit weird, isn't it? I hope it's needless to say that script will be executed in another session created by DBMS_SCHEDULER.
===SQL===
2. You can inline code into SQL as well, but, again, query must compile after inlining.
hello.sql
'HELLO' || ' ' ||
SQL> select
2 #hello.sql
3 rownum
4 from dual
5 connect by rownum <= 3;
HELLO 1
HELLO 2
HELLO 3
SQL>
SQL> select
2 #START hello.sql
3 rownum
4 from dual
5 connect by rownum <= 3;
HELLO 1
HELLO 2
HELLO 3
===SPOOL===
3. Finally, you can generate in a loop what you need, spool it and execute it.
You can use either SQL or PL/SQL to generate the script. Example below shows SQL approach.
main_spool.sql
set echo off;
set pagesize 0;
spool tmp.sql
select
'#simple_sample.sql' || ' ' || rownum x
from dual
connect by rownum <= 3;
spool off
#tmp.sql
SQL> #main_spool
#simple_sample.sql 1
#simple_sample.sql 2
#simple_sample.sql 3
HELLO 1
HELLO 2
HELLO 3

Related

Difference between bulk collect and cursor

I wonder what is the difference between using a bulk collect and then loop on the result and using a cursor. And subquestion : Are there cases where the only option is a cursor?
Thanks
Oracle implicitly optimizes PL/SQL for-cursor-loop to use fetches by 100 rows, so it's similar to bulk collect limit 100.
Simple example:
SQL> begin
2 for r in (select/*+findme*/ level n from dual connect by level<=100) loop
3 exit;
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> select fetches, rows_processed,sql_text
2 from v$sql
3 where lower(sql_text) like 'select/*+findme*/%';
FETCHES ROWS_PROCESSED SQL_TEXT
---------- -------------- ----------------------------------------------------------
1 100 SELECT/*+findme*/ LEVEL N FROM DUAL CONNECT BY LEVEL<=100
1 row selected.
NB: Such optimisation works only if plsql_optmize_level parameter>=2 (default=2). In case of plsql_optmize_level < 2, for-loop fetches by 1 row. You can try to change it and check the difference.
PS. + You can't use a cursor for-loop with dynamic sql

How can I skip printing an SQL script but still show the DBMS values?

I'm developing a code and would like to see in the results only the actual values, instead of the script I executed.
So I'm trying to develop a "table rows counter" and I've been quite successful, but I still get the full executed script, when I only need its results.
Let's say, my code is:
DECLARE
VAR_1 NUMBER := 0;
BEGIN
SELECT IMITM INTO VAR_1 FROM PRODDTA.F4101 WHERE ROWNUM <= 1;
DBMS_OUTPUT.PUT_LINE('DUMMY TEST 1' || VAR_1);
DBMS_OUTPUT.PUT_LINE('DUMMY TEST 2');
END;
/
Well, by clicking F5 I would see:
DUMMY TEST 150041087
DUMMY TEST 2
PL/SQL procedure successfully completed.
In the complete code it reads about 200 lines of only code, so...
How can I get only the output lines, and in this case, remove the "PL/SQL procedure..." legend?
I've tried unsuccessfully yo use "SET FEED" and "SET TERM" off.
According to Oracle documentation:
SET FEEDBACK OFF also turns off the statement confirmation messages
such as 'Table created' and 'PL/SQL procedure successfully completed'
that is displayed after successful SQL or PL/SQL statements.
You need to use SET FEEDBACK OFF as following:
SQL> SET SERVEROUT ON
SQL> SET FEEDBACK OFF
SQL> DECLARE
2 VAR_1 NUMBER := 0;
3 BEGIN
4 SELECT
5 1
6 INTO VAR_1
7 FROM
8 DUAL
9 WHERE
10 ROWNUM <= 1;
11
12 DBMS_OUTPUT.PUT_LINE('DUMMY TEST 1' || VAR_1);
13 DBMS_OUTPUT.PUT_LINE('DUMMY TEST 2');
14 END;
15 /
DUMMY TEST 11
DUMMY TEST 2
SQL>
SQL>
Cheers!!
I actually managed to do it by exploring SET commands.
For the SQL script "acknolwedgement", we can skip it with the following commands:
To disable the SQL & variables verification before the actual execution, use:
SET VERIFY OFF
To disable the post execution notes, as Tejash indicated already, use:
SET FEEDBACK OFF
So my prologue ends up being:
SET SERVERTOUTPUT ON;
SET VERIFY OFF;
SET FEEDBACK OFF;
Thanks a lot for the answers & comments.

PL/SQL IF statement and variable declaration

I am trying to make the code posted below work, but I am receiving an error about the IF statement:
DECLARE
a number;
BEGIN
SELECT Pers_API.Is_Manager_(10018) INTO a from dual;
EXCEPTION
WHEN no_data_found THEN a := 0;
END;
IF (a >0) THEN
SELECT 1 from dual;
ELSE
SELECT 0 from dual;
END IF;
In the first block I am declaring an 'A' variable and setting it to 0 (by the way, the API call result will be 0 or 1, nothing else). The first block works fine or - at least - I do think so. But the IF block is not working and having this error:
PLS-00103:Encountered the symbol "IF"
Any help is appreciated.
Update: I have tried it this way:
DECLARE
a number:=0;
BEGIN
SELECT Pers_API.Is_Manager_(10018) INTO a from dual;
IF (:a >0) THEN
SELECT 1 from dual;
ELSE
SELECT 0 from dual;
END IF;
END;
I received the exception below:
ORA-01008: not all variables bound
Update
In other words, what I am trying to do is:
if Pers_API.Is_Manager_(10018) returns true (as 1), I have to do another select statement
if it's false (as 0), I have to return null.
Any other ideas are appreciated.
A few objections, if I may.
there's no need to select from dual in order to assign a value to a variable, when that value is returned by a function - simply assign it
exception handler seems to be superfluous. You said that the is_manager_ function returns 0 or 1 and nothing else. Therefore, it'll always return something, so - why do you expect that SELECT to return NO_DATA_FOUND? Besides, such a case should be handled by the function itself (i.e. you have to make sure that it returns 0 or 1 and nothing else)
the last paragraph you wrote is somewhat strange. You said that - if the function returns 1, you have to run another SELECT statement. Otherwise, you have to return null (while code you wrote, select 0 ... suggests a zero (0), not null); so, which one is true?
also, saying that you have to return something (null, 0, whatever) suggests that piece of code you wrote should be a function that returns a value. Is that so?
Code that actually compiles might look like in this example.
First, a function (based on Scott's schema) that checks whether an employee is a manager:
SQL> create or replace function is_manager_ (par_empno in number)
2 return number
3 is
4 /* function checks whether PAR_EMPNO belongs to a manager and returns:
5 - 1 - employee is a manager
6 - 0 - employee is NOT a manager
7 */
8 retval number := 0;
9 begin
10 select max(1)
11 into retval
12 from emp e
13 where mgr = par_empno;
14
15 return retval;
16 exception
17 when no_data_found then
18 return retval;
19 end;
20 /
Function created.
SQL>
Code you posted, rewritten so that it does something:
if an employee is a manager, returns his/her salary
otherwise, it does not
SQL> declare
2 a number := is_manager_(&&l_empno);
3 l_sal emp.sal%type;
4 begin
5 if a > 0 then
6 select sal
7 into l_sal
8 from emp
9 where empno = &&l_empno;
10 dbms_output.put_line('Employee is a manager and his/her salary is ' || l_sal);
11 else
12 null;
13 dbms_output.put_line('Employee is not a manager');
14 end if;
15 end;
16 /
Enter value for l_empno: 7698
Employee is a manager and his/her salary is 2850
PL/SQL procedure successfully completed.
SQL> undefine l_empno
SQL> /
Enter value for l_empno: 7369
Employee is not a manager
PL/SQL procedure successfully completed.
SQL> undefine l_empno
SQL> /
Enter value for l_empno: -1
Employee is not a manager
PL/SQL procedure successfully completed.
SQL>
See if you can adjust it so that it fits your needs. If not, do answer to my objections posted at the beginning of this message, and we'll see what to do next.
It appears to me that what you are really trying to achieve here is to run a query which must use the result obtained from the package function as a condition. If so, the PL/SQL block wouldn't be needed and can be written as a basic select operation.
What is still not clear from your explanation is whether your select query
SELECT 1 from dual is the actual query you are about to use in your code.Generally, in the select statement, we use CASE expression to evaluate the conditional logic, which essentially does what IF ELSE condition was supposed to do to return the desired result.The query would have a structure of this form,
SELECT
CASE
WHEN pers_api.is_manager_(10018) = 1 THEN some_expression
ELSE some_other_expression --If you omit the else condition, it defaults to null when all others are false
END
END
as
FROM DUAL;
More specifically, if you want to have the resultant expression come from another database table, the queries can take the form
SELECT
CASE
WHEN pers_api.is_manager_(10018) = 1 THEN some_expression
ELSE some_other_expression --If you omit the else condition, it defaults to null when all others are false
END
as
FROM table_to_run_query
WHERE where_clauses = some_values;
If you say that your resultant expression from the table involves many other constructs, the challenge would be to compose it in a single query cleverly to avoid any PL/SQL at all. That would be achievable, but it will require that you explain us with some sample data and expected result, as to what exactly you want,preferably in a new question.
It would not be complete if I don't mention the REFCURSOR technique to obtain results from a select query in PL/SQL. It is either through DBMS_SQL.RETURN_RESULT(Oracle 12c and above) or using a REFCURSOR bind variable running it in SQL* Plus or as Script in other tools (F5).
VARIABLE x refcursor;
DECLARE BEGIN
IF
pers_api.is_manager_(10018) = 1
THEN
OPEN :x FOR SELECT some_columns FROM some_tables;
ELSE
OPEN :x FOR SELECT some_columns FROM other_tables;
END IF;
--DBMS_SQL.RETURN_RESULT(:x); --12c and above
END;
/
PRINT x -- 11g

sql plus warning in procedure. procedure created with compilation error. (procedure with parameter)

hey i am trying to do a pl sql program with the help of procedure. i want to check if number given by user is even or odd using procedure but i am getting an warning : procedure created with compilation error .
create or replace procedure even( a in out number)
as n number :=&n;
begin
if(n,2)=0 then
dbms_output.put_line('even');
else
dbms_output.put_line('odd');
end if;
end;
/
It is meaningless to compile the procedure each time you get user input.You should rather be doing the following.
Compile the procedure without any substitution variables. The parameter should be just IN and not IN OUT unless you want to modify its value inside the procedure.
CREATE OR replace PROCEDURE Even(n IN NUMBER)
AS
BEGIN
IF MOD(n, 2) = 0 THEN
dbms_output.put_line('even');
ELSE
dbms_output.put_line('odd');
END IF;
END;
/
Then execute this compiled procedure as many times as you like by passing user input.
SQL> SET SERVEROUTPUT ON;
SQL> EXEC even ( &n );
Enter value for n: 5
odd
PL/SQL procedure successfully completed.
SQL> EXEC even ( &n );
Enter value for n: 4
even
PL/SQL procedure successfully completed.
Why don't you use you mod function:
if mod(a,2)=0 then
dbms_output.put_line('even');
else
dbms_output.put_line('odd');
end if;
I think you put "n" instead of "a"
Consider using a function instead; in my opinion, it is a better option for such a task than a procedure.
A natural choice would be a function that returns Boolean:
SQL> create or replace function f_is_even (par_n in number)
2 return boolean
3 is
4 begin
5 return mod(par_n, 2) = 0;
6 end;
7 /
Function created.
You'd then use it in some PL/SQL code as the following example (yes, it looks stupid because it appears that it does exactly what your procedure does, but note - this is just an example; in real life, you'd use it in smarter way):
SQL> begin
2 if f_is_even(6) then
3 dbms_output.put_Line('even');
4 else
5 dbms_output.put_Line('odd');
6 end if;
7 end;
8 /
even
PL/SQL procedure successfully completed.
Drawback of such a function is that you can't use it in SQL (but, as I said, PL/SQL):
SQL> select f_is_even(5) from dual;
select f_is_even(5) from dual
*
ERROR at line 1:
ORA-06552: PL/SQL: Statement ignored
ORA-06553: PLS-382: expression is of wrong type
A possible "workaround" is to create a procedure that doesn't return Boolean but, for example, number (0 for "false" and 1 for "true") or string (N for "false, no" and Y for "true, yes"). For example:
SQL> create or replace function f_is_even_01 (par_n in number)
2 -- returns 1 if number is even; returns 0 if number is odd
3 return number
4 is
5 begin
6 return case when mod(par_n, 2) = 0 then 1
7 else 0
8 end;
9 end;
10 /
Function created.
SQL> select f_is_even_01(5) r1,
2 f_is_even_01(6) r2
3 from dual;
R1 R2
---------- ----------
0 1
There's nothing wrong in using a procedure; I just thought that you might want to hear another opinion.

Declare cursor in sqlplus command mode

I have an SQL file which uses declares a cursor and I am running it using #abc, but it did not execute all statements and waiting without returning to command prompt. It did not proceed after declare cursor statement. When I tried to run the declare cursor statement in command mode, the same problem is happening again. I am able to return to SQL priompt only after pressing Ctrl + C. I am very new to SQL world. Though this could be a basic mistake, I am not able to find out the solution in any site. Any help is greatly appreciated.
SQL> DECLARE CURSOR id_cursor IS SELECT id FROM user_names WHERE dept_no = 1002
AND BITAND(flags, 4) = 4 AND time_created BETWEEN 1137974400 AND 1326067199;
2
3
4 ;
5
6
All DECLARE and BEGIN blocks in SQL*Plus need to be ended with a / on a new empty line:
SQL> DECLARE
2 CURSOR c IS SELECT 1 FROM DUAL;
3 BEGIN
4 NULL;
5 END;
6 /
PL/SQL procedure successfully completed.
Without this / SQL*Plus has no way to know that your statement has ended (so in your example it waits for user input).
After the ; type a / and enter. This will run your PL/SQL statement. But the sample you've given does nothing but declare a cursor. You must then use it like so:
declare
cursor ID_CURSOR is
select ID
from USER_NAMES
where DEPT_NO = 1002
and bitand(FLAGS, 4) = 4
and TIME_CREATED between 1137974400 and 1326067199;
begin
for REC in ID_CURSOL loop
<do something with your data>;
end loop;
end;
/

Resources