Cursor in a Procedure - oracle

I am getting the following error for the code
ORA-06550: line 17, column 0: PLS-00103: Encountered the symbol
"end-of-file" when expecting one of the following:
declare
procedure empsindept (dep_no in emp.deptno%type) is
totalnum number := 0;
begin
dbms_output.put_line ("The guys in dept are");
for i in (select * from emp where deptno=dep_no) loop
dbms_output.put_line(i.ename||"--------"||i.empno);
totalnum:=totalnum+1;
end loop;
dbms_output.put_line("The total guys are : "||totalnum);
end;
Please help me here
Thanks You

When you use declare you must complete the PL/SQL block, as stated in the docs:
PL/SQL is a block-structured language. A PL/SQL block is defined by the keywords DECLARE, BEGIN, EXCEPTION, and END, which break up the block into three sections:
Declarative: statements that declare variables, constants, and other code elements, which can then be used within that block
Executable: statements that are run when the block is executed
Exception handling: a specially structured section you can use to “catch,” or trap, any exceptions that are raised when the executable section runs
Only the executable section is required. You don’t have to declare anything in a block, and you don’t have to trap exceptions raised in that block.
So... your code should look like this, with an additional begin ... end at the end, in which you also will want to execute some statements, probably calling your procedure:
declare
procedure empsindept (dep_no in emp.deptno%type) is
totalnum number := 0;
begin
dbms_output.put_line ('The persons in dept ' || dep_no || ' are');
for i in (select * from emp where deptno=dep_no) loop
dbms_output.put_line(i.ename||'--------'||i.empno);
totalnum:=totalnum+1;
end loop;
dbms_output.put_line('The total number of persons is: '||totalnum);
end;
begin
-- something should happen here...
end;

Related

Ignore lines that causes errors

I have a big Oracle script with thousands of package call inside a BEGIN - END;
Is there a way to ignore the lines that causes error and continue executing the next lines? Some sort of "On Error Resume Next" in vb.
If you have only one BEGIN END section, then you can use EXCEPTION WHEN OTHERS THEN NULL.
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
exception when others then null;
end;
SQL> /
PL/SQL procedure successfully completed.
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
--exception when others then null;
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 6
SQL>
The whole concept of "ignore errors" is a bug, and a lie if any errors occur. That is not to say you cannot trap errors and continue processing, just that you MUST handle the errors. For example, assume the use case: "Data has been loaded into a stage table from multiple .csv files. Now load into the tables A and Table B according to ....".
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, col_a2);
insert into table_b (col1, col2)
values (rec.col_b1, col_b2);
exception
when others then null;
end;
end loop;
process_message := 'Load Tables A,B Complete';
end ;
Now suppose a user created the a .csv file entered "n/a" in numeric columns where there was no value or the value was unknown. The result of this all too common occurrence is all such rows were not loaded, but you have no way to know that until the user complains their data was not loaded even though you told them it was. Further you have no way of determining the problem.
A much better approach is to "capture and report".
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
load_error_occurred boolean := False;
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, rec.col_a2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
begin
insert into table_b (col1, col2)
values (rec.col_b1, rec.col_b2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
end loop;
if load_error_occurred then
process_message := 'Load Tables A,B Complete: Error(s) Detected';
else
process_message := 'Load Tables A,B Complete: Successful No Error(s)';
end if;
end Load_Tables_A_B_from_Stage ;
Now you have informed the user of the actual status, and where you are contacted you can readily identify the issue.
User here is used in the most general sense. It could mean a calling routine instead of an individual. Point is you do not have to terminate your process due to errors but DO NOT ignore them.
I don't think there is any magic one-liner that will solve this.
As others have, use a editor to automate the wrapping of each call within a BEGIN-EXCEPTION-END block might be quicker/easier.
But, if feel a little adventurous, or try this strategy:
Let's assume you have this:
BEGIN
proc1;
proc2;
proc3;
.
.
.
proc1000;
END;
You could try this (untested, uncompiled but might give you an idea of what to try):
DECLARE
l_progress NUMBER := 0;
l_proc_no NUMBER := 0;
e_proc_err EXCEPTION;
-- A 'runner' procedure than manegrs the counters and runs/skips dpending on these vals
PROCEDURE run_proc ( pname IN VARCHAR2 ) IS
BEGIN
l_proc_no := l_proc_no + 1;
IF l_proc_no >= l_progress
THEN
-- log 'Running pname'
EXECUTE IMMEDIATE 'BEGIN ' || pname || '; END;' ;
l_progress := l_progress + 1;
ELSE
-- log 'Skipping pname'
END IF;
EXCEPTION
WHEN OTHERS THEN
-- log 'Error in pname'
l_progress := l_progress + 1;
RAISE e_proc_err;
END;
BEGIN
l_progress := 0;
<<start>>
l_proc_no := 0;
run_proc ( 'proc1' );
run_proc ( 'proc2' );
run_proc ( 'proc3' );
.
.
run_proc ( 'proc1000' );
EXCEPTION
WHEN e_proc_err THEN
GOTO start;
WHEN OTHERS THEN
RAISE;
END;
The idea here is to add a 'runner' procedure to execute each procedure dynamically and log the run, skip, error.
We maintain a global count of the current process number (l_proc_no) and overall count of steps executed (l_progress).
When an error occurs we log it, raise it and let it fall into the outer blocks EXCEPTION handler where it will restart via an (evil) GOTO.
The GOTO is placed such that the overall execution count is unchanged but the process number is reset to 0.
Now when the run_proc is called it sees that l_progress is greater than l_proc_no, and skips it.
Why is this better than simply wrapping a BEGIN EXCEPTION END around each call?
It might not be, but you make a smaller change to each line of code, and you standardise the logging around each call more neatly.
The danger is a potential infinite loop which is why I specify e_proc_err to denote errors within the called procedures. But it might need tweaking to make it robust.

getting error while compiling the stored procedure

I am writing this below stored procedure but i am also getting the exception while compiling the procedure in oracle, below is the procedure
CREATE OR REPLACE PACKAGE BODY TEST_TABLE AS
PROCEDURE TEST_TABLE
--This procedure will delete partitions for the following tables:
--TEST_TABLE
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEST_TABLE'
)
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', 3) THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEST_TABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
END;
/
and the exception that I am getting it while compiling is Please advise how to overcome from this.
Error(7,1): PLS-00103: Encountered the symbol "BEGIN" when expecting one of the following: ( ; is with authid as cluster order using external deterministic parallel_enable pipelined result_cache The symbol ";" was substituted for "BEGIN" to continue.
Error(22,25): PLS-00103: Encountered the symbol "DROP" when expecting one of the following: * & = - + ; < / > at in is mod remainder not rem return returning <an exponent (**)> <> or != or ~= >= <= <> and or like like2 like4 likec between into using || bulk member submultiset
You need to make correct quotation as below( and one more is keyword just after PROCEDURE TEST_TABLE "thanks to Alex who make me awaken" ) :
CREATE OR REPLACE PACKAGE BODY PKG_TEST_TABLE IS
PROCEDURE TEST_TABLE IS
--This procedure will delete partitions for the following tables:
--TEST_TABLE
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEST_TABLE'
)
LOOP
BEGIN
IF sysdate >= ADD_MONTHS(cc.high_value, 3) THEN
EXECUTE IMMEDIATE
'ALTER TABLE TEST_TABLE DROP PARTITION ' || cc.partition_name;
Dbms_Output.Put_Line('Dropping partition is completed.');
END IF;
END;
END LOOP;
EXCEPTION WHEN Others THEN Dbms_Output.Put_Line( SQLERRM );
END TEST_TABLE;
END PKG_TEST_TABLE;
/
As a little suggestion use a different name for package than procedure such as PKG_TEST_TABLE against confusion.
Edit : of course you need to create a specification part for a package before the body part of the package :
CREATE OR REPLACE PACKAGE PKG_TEST_TABLE IS
PROCEDURE TEST_TABLE;
END PKG_TEST_TABLE;
/
The first error message tells you something is missing before BEGIN, and even mentions the two possible options in the 'when expecting' list. You need to change it to:
PROCEDURE TEST_TABLE IS
-- ^^
Or you can use AS instead of IS if you prefer...
The second error is because you have a string literal embedded in your dynamic SQL and you haven't escaped the single quotes, though you have elsewhere:
...
dbms_output.put_line(''drop partition completed'');
-- ^ ^
END;';
You could use the alternative quoting mechanism instead.
I'm not sure why you're doing two levels of dynamic SQL; you can do the dbms_output() and evaluate cc.high_value statically, and decide whether to make the alter call, with only that part dynamic (as #BarbarosÖzhan has shown, so I wont repeat that!). Or do the high-value check within the cursor query.
I am still getting exception Error(1,14): PLS-00304: cannot compile body of 'TEST_TABLE' without its specification
If you want a package then you have to create its specification before you try to create its body:
CREATE OR REPLACE PACKAGE TEST_TABLE AS
PROCEDURE TEST_TABLE;
END TEST_TABLE;
/
CREATE OR REPLACE PACKAGE BODY TEST_TABLE AS
PROCEDURE TEST_TABLE IS
BEGIN
FOR cc IN
...
LOOP
...
END LOOP;
END TEST_TABLE; -- end of procedure
END TEST_TABLE; -- end of package
/
Having the package name the same as a procedure within it is a bit odd and confusing.
But maybe you didn't actually want a package at all, and were trying to create a standalone procedure, in which case just remove the package-body part:
CREATE OR REPLACE PROCEDURE TEST_TABLE AS
BEGIN
FOR cc IN
...
LOOP
...
END LOOP;
END TEST_TABLE; -- end of procedure
/
Read more.
I would strongly suggest you get rid of the exception handler, and I've left that out of those outlines - you should let any exception flow back to the caller. You don't know that whoever calls this will even have output enabled, so might well not even see the message you're printing instead. Only ever catch exceptions you can handle and need to handle at that point.

Why are none of my conditions being met in this proc?

So i have a stored procedure (that's been watered down below for demo purposes) that aren't passing any conditions and thus aren't inserting/passing any values into my table. I've tried converting the varchar/string that is being passed in by Java to a number but nothing is working. Below is my 'simplified code'
Create or Replace Procedure SAMPLE(rValue IN VARCHAR)
IS
v_Max value.value%type;
v_forecast value.value%type;
BEGIN
--
SELECT BUFFER_MAX_VALUE
INTO v_MAX
FROM look_up;
--
EXCEPTION
WHEN no_data_found
THEN SELECT 0
INTO v_forecast
FROM DUAL;
--
IF to_Number(rValue) < 0 OR to_Number(rValue) > v_MAX)
THEN
dbms_output.put_line('IF1 Works');
insert into value(value_id, value)
values(1, rValue);
ELSIF rValue is null OR to_Number(rValue) = 0
THEN
dbms_output.put_line('IF1A ONLY Works');
END IF;
ELSE
insert into value(value_id, value)
values(1, v_forecast);
dbms_output.put_line('IF1 ELSE ONLY Works');
END SAMPLE;
i tried passing the following in:
BEGIN
SAMPLE('-7');
END;
If the first SELECT BUFFER_MAX_VALUE returns anything, nothing else will be executed because you put absolutely everything into the EXCEPTION section. If you meant to handle that statement only, you should have enclosed it into its own BEGIN-END block, such as
create procedure ...
begin
-- its own begin starts now
begin
select buffer_max_value into v_max
from look_up;
exception
when no_data_found then
-- do something here
end;
-- its own end ends now
-- put the rest of your code here
end;
By the way, does LOOK_UP table contain no rows or only one row, always? Because, as SELECT you wrote contains no WHERE clause, it might raise TOO_MANY_ROWS (which you should also handle).
You declared rValue as VARCHAR2, and then apply TO_NUMBER to it. Why don't you declare it to be a NUMBER, instead? Because, nothing prevents you from passing, for example, 'XYZ' to the procedure, and then TO_NUMBER will miserably fail with the INVALID NUMBER error.
[EDIT: some more exception handling]
EXCEPTION section handles all exceptions that happen in that BEGIN-END block, no matter how many SELECT statements you have. Though, you won't know which one failed, unless you include a little bit of additional (simple) programming.
Note that this is just for showing what I meant; don't handle errors with DBMS_OUTPUT (as, most probably, nobody will see it), and rarely you'd want to handle errors with WHEN OTHERS.
create procedure ...
l_position number;
begin
l_position := 1;
select ... into ... from ...;
l_position := 2;
select ... into ...
exception
when others then
dbms_output.put_line('Error on position ' || l_position ||' '|| sqlerrm);
raise;
end;
As far as I can tell, you wanted the exception section to trap the situation where there is nothing in the lookup table. In that case, you set v_forecast and then continue. That means you need to put the select inside its own block.
I also avoiding multiple to_number calls by setting a constant.
I got rid of the unnecessary select from dual.
I also really really hope that you do not have a table named VALUE with a column named VALUE. Choose more meaningful names.
See how this works for you.
CREATE OR REPLACE PROCEDURE sample (rvalue IN VARCHAR2)
IS
c_rvalue CONSTANT NUMBER := rvalue;
v_max VALUE.VALUE%TYPE;
v_forecast VALUE.VALUE%TYPE;
BEGIN
BEGIN
SELECT buffer_max_value INTO v_max FROM look_up;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
v_forecast := 0;
END;
IF c_rvalue < 0 OR c_rvalue > v_max
THEN
DBMS_OUTPUT.put_line ('IF1 Works');
INSERT INTO VALUE (value_id, VALUE)
VALUES (1, rvalue);
ELSIF c_rvalue IS NULL OR c_rvalue = 0
THEN
DBMS_OUTPUT.put_line ('IF1A ONLY Works');
ELSE
INSERT INTO VALUE (value_id, VALUE)
VALUES (1, v_forecast);
DBMS_OUTPUT.put_line ('IF1 ELSE ONLY Works');
END IF;
END sample;

How to execute one procedure within another procedure

Hi I am writing one procedure which will be called by the program and this procedure will further call to another procedure to perform different business logic. so I did something like this.
PROCEDURE calculator(service_id IN NUMBER, amount IN NUMBER) as
p_proc_name varchar(100);
begin
select sc.procedure_name into p_proc_name from test.procedure sc where sc.service_config_id = service_id;
begin
execute immediate (p_proc_name) using 1;
exception when NO_DATA_FOUND then
DBMS_OUTPUT.PUT_LINE('p_proc_name = ' || p_proc_name);
end;
end sb_referal_calculator;
PROCEDURE f_service(amount IN NUMBER) as
cmpany_id NUMBER;
service_date date;
leases_days NUMBER;
referal_amount Number;
requested_quote_id number :=1;
begin
referal_amount :=0;
DBMS_OUTPUT.PUT_LINE('service_date = ');
end f_service;
PROCEDURE d_service(amount IN NUMBER) as
cmpany_id NUMBER;
service_date date;
leases_days NUMBER;
referal_amount Number;
requested_quote_id number :=1;
begin
referal_amount :=0;
DBMS_OUTPUT.PUT_LINE('service_date = ');
end d_service;
So here calcultor procedure will find the another procedure name dynamically and try to execute it with parameter. But it gives an error.
It is just a test program.
Executing PL/SQL: CALL DBMS_DEBUG_JDWP.CONNECT_TCP( '10.1.26.70', '55891' )
Debugger accepted connection from database on port 55891.
ORA-00900: invalid SQL statement
ORA-06512: at "test.demo_pkg", line 38
ORA-06512: at line 8
Executing PL/SQL: CALL DBMS_DEBUG_JDWP.DISCONNECT()
Process exited.
I really do not how this procedure will work to perform this task. I remembered it was running and I was doing testing. But really do not what i have did and stop working.
Please correct me what i doing wrong.
Thanks
When you use execute immediate it runs the dynamic statement in an SQL context that isn't able to see your PL/SQL context. That has several impacts here. Firstly, you have to call your procedure from PL/SQL so you need to create an anonymous block, as Egor Skriptunoff said, and exactly the format you need depends on what the table (and thus your vaiable) contains. The shortest it might be is:
execute immdiate 'begin ' || p_proc_name || ' end;' using 1;
But that assumes the varible contains a value like:
test_pkg.d_service(:arg);
If it only contains the name of the procedure with no arguments and no package qualifier, i.e. just d_service, it might need to be as much as:
execute immdiate 'begin test_pkg.' || p_proc_name || '(:arg); end;' using 1;
Or something in between.
The other impact is that the procedure name has to be public as it is effectively being called from outside the package when it's invoked dynamically; so it has to be declared in the package specification. That may already be the case here from the order the procedures are appearing in the body.
But if you are always calling procedures in the same package, and since you must then have a limited number of possible values, it might be simpler to avoid dynamic SQL and use the value to decide which procedure to call:
case p_proc_name
when 'f_service' then
f_service(1);
when 'd_service' then
d_service(1);
-- etc.
end case;
That also lets you call private procedures.

Variable in UPDATE oracle in procedure : invalid identifier

I don't understand why service is complaining with
Fehler(36,11): PL/SQL: ORA-00904: "FOUND_VP": invalid identifier
Variable is declared in the first begin...
Is it not possible to use variable directly in queries ?
when trying store following procedure :
create or replace PROCEDURE fpwl_update_vp(
my_zn IN NUMBER, my_verwaltung IN VARCHAR2 , my_variante IN NUMBER, my_vp IN NUMBER
) IS
BEGIN
DECLARE
search_VP IFT_INFO_LAUF.VP%TYPE;
found_VP IFT_INFO_LAUF.VP%TYPE;
INFOversion number := 25;
BEGIN -- search SYFA_VP
SELECT SYFA_VP
INTO found_VP
FROM FPWL_VP_MAPPING
WHERE INFO_VP=search_VP ;
exception
when no_data_found then
dbms_output.put_line ('Kein SYFA VP : Importiere aus SYFA');
--found_VP:=:=cus_info25.pa_info_data.fn_insert_syfa_vp(my_vp,25);
WHEN OTHERS THEN
ROLLBACK;
RETURN;
END; -- SYFA VP
-- Update VP
UPDATE IFT_INFO_LAUF
SET vp = found_VP
WHERE id_kopf IN
(SELECT id_kopf
FROM ift_info_kopf
WHERE fahrtnummer= my_zn
AND verwaltung= my_verwaltung
AND variante = my_variante
)
;
--COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END ;
Your problem is that found_VP is going out of scope.
Move the contents of the "DECLARE" block to just after the "IS":
create or replace PROCEDURE fpwl_update_vp(
my_zn IN NUMBER, my_verwaltung IN VARCHAR2 , my_variante IN NUMBER, my_vp IN NUMBER
) IS
search_VP IFT_INFO_LAUF.VP%TYPE;
found_VP IFT_INFO_LAUF.VP%TYPE;
INFOversion number := 25;
BEGIN
BEGIN -- search SYFA_VP
etc
Make sure that
FPWL_VP_MAPPING.SYFA_VP
is the same type with
IFT_INFO_LAUF.VP
and make sure that
SELECT SYFA_VP INTO found_VP FROM FPWL_VP_MAPPING WHERE INFO_VP=search_VP ;
does not return multiple rows.
But I doubt that is the case with the error that you have given.
Since the error message refers to line 36 and the reference to found_VP in your code sample is on line 18, you've omitted the part of the code that actually has the problem.
It looks like you have a scope problem; you're declaring found_VP in an inner block (one level of DECLARE/BEGIN/END) and referring to it outside that block, either in the parent block or another one at the same level. The issue isn't where you're selecting into found_VP, it's (I think) that you're referring to it again later on, beyond the code you've posted, and therefore outside the block the variable is declared in.
To demonstrate, I'll declare l_name in an inner block, as you seem to have done:
create or replace procedure p42 is
begin
declare
l_name all_tables.table_name%TYPE;
begin
select table_name
into l_name -- this reference is OK
from all_tables
where table_name = 'EMPLOYEES';
end;
select table_name
into l_name -- this reference errors
from all_tables
where table_name = 'JOBS';
end;
/
Warning: Procedure created with compilation errors.
show errors
Errors for PROCEDURE P42:
LINE/COL ERROR
-------- -----------------------------------------------------------------
12/2 PL/SQL: SQL Statement ignored
13/7 PLS-00201: identifier 'L_NAME' must be declared
14/2 PL/SQL: ORA-00904: : invalid identifier
Notice that the error is reported against line 13, which is in the outer block; it doesn't complain about it in the inner block because it is in-scope there.
So, you need to declare the variable at the appropriate level. As Colin 't Hart says that is probably right at the top, between the IS and the first BEGIN, as that is the procedure-level DECLARE section (it doesn't need an explicit DECLARE keyword).

Resources