PLS-00103 error when trying to fetch from a cursor - oracle

I'm trying to write a cursor. I try to match the syntax of examples but always getting compile failure on the FETCH statement.
CREATE OR REPLACE PROCEDURE IFSAPP.CLEAR_OLD_PURCHASE_ORDERS (cPlannedDelDate in varchar2) IS
-- cursor to get all the purchase orders's that have lines in released state that
CURSOR c1 IS
SELECT DISTINCT PO.ORDER_NO
FROM PURCHASE_ORDER PO, PURCHASE_ORDER_LINE_NOPART POLN
WHERE PO.ORDER_NO = POLN.ORDER_NO
AND POLN.STATE = 'Released'
AND POLN.PLANNED_DELIVERY_DATE < TO_DATE(cPlannedDelDate, 'DD/MM/YYYY');
BEGIN
DECLARE corder_no varchar2(12);
OPEN c1;
LOOP
FETCH c1 INTO corder_no;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(corder_no);
END LOOP;
CLOSE c1;
END CLEAR_OLD_PURCHASE_ORDERS;
/
LINE/COL ERROR
-------- -----------------------------------------------------------------
17/9 PLS-00103: Encountered the symbol "FETCH" when expecting one of the following:
constant exception <an identifier>
<a double-quoted delimited-identifier> table columns long
double ref char time timestamp interval date binary national
character nchar
21/5 PLS-00103: Encountered the symbol "CLOSE" when expecting one of the following:
end not pragma final instantiable order overriding static
member constructor map
Can anyone see where I am going wrong?

The problem is actually where you're declaring your local variable, and the use of the DECLARE keyword. That's starting a new inner PL/SQL block, but you then have the OPEN etc. without continuing that pattern with a new BEGIN.
You don't need a sub-block though, just move the local variable declaration up before the existing BEGIN, and lose the extra DECLARE:
CREATE OR REPLACE PROCEDURE IFSAPP.CLEAR_OLD_PURCHASE_ORDERS (cPlannedDelDate in varchar2) IS
-- cursor to get all the purchase orders's that have lines in released state that
CURSOR c1 IS
SELECT DISTINCT PO.ORDER_NO
FROM PURCHASE_ORDER PO, PURCHASE_ORDER_LINE_NOPART POLN
WHERE PO.ORDER_NO = POLN.ORDER_NO
AND POLN.STATE = 'Released'
AND POLN.PLANNED_DELIVERY_DATE < TO_DATE(cPlannedDelDate, 'DD/MM/YYYY');
corder_no varchar2(12);
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO corder_no;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(corder_no);
END LOOP;
CLOSE c1;
END CLEAR_OLD_PURCHASE_ORDERS;
/
Incidentally, you should consider using ANSI join syntax, not the ancient comma-separated-FROM clause syntax. And it would be simpler to use an implicit cursor loop:
CREATE OR REPLACE PROCEDURE IFSAPPCLEAR_OLD_PURCHASE_ORDERS (cPlannedDelDate in varchar2) IS
BEGIN
FOR r1 IN (
SELECT DISTINCT PO.ORDER_NO
FROM PURCHASE_ORDER_LINE_NOPART POLN
JOIN PURCHASE_ORDER PO
ON PO.ORDER_NO = POLN.ORDER_NO
WHERE POLN.STATE = 'Released'
AND POLN.PLANNED_DELIVERY_DATE < TO_DATE(cPlannedDelDate, 'DD/MM/YYYY')
) LOOP
DBMS_OUTPUT.PUT_LINE(r1.order_no);
END LOOP;
END CLEAR_OLD_PURCHASE_ORDERS;
/
I'd also generally prefer to have the procedure argument declared as the data type you need, i.e. as a DATE, so you can use that in your query without converting it; and make it the caller's problem to pas the correct data type in.

Related

I'm getting a 'Procedure created with compilation errors.'

I'm a beginner to sql and i'm getting a compilation error, can anyone pls guide me with my mistake in the code.
set serveroutput on;
create or replace procedure p1(n in varchar) as
cursor c1 is
select e_name, inv_amount
from employee, investment
where employee.e_id=investment.e_id
and inv_amount=50000;
c c1 %rowtype;
begin
open c1;
dbms_output.put_line('e_name'||"||'inv_amount');
loop
fetch c1 into c;
exit when c1 %notfound;
if(c.inv_amount=n)then
dbms_output.put_line(c.e_name||c.inv_amount);
end if;
end loop;
close c1;
end;
/
I can see some potential errors in your code. Which must be fixed as -
set serveroutput on;
create or replace procedure p1(n in varchar)
AS
cursor c1 is
select e_name,inv_amount
from employee,investment
where employee.e_id=investment.e_id
and inv_amount=50000;
c c1%rowtype; -- No space between cursor and ROWTYPE keyword
begin
open c1;
dbms_output.put_line('e_name'||''||'inv_amount'); -- It must be 2 single quotes instead of 1 double quotes
loop
fetch c1 into c;
exit when c1%notfound; -- Remove space
if(c.inv_amount=n)then
dbms_output.put_line(c.e_name||c.inv_amount);
end if;
end loop;
close c1;
end;
/
You can simplify this quite a lot:
You only care about rows where inv_amount = n, so why not add that as a filter condition in the cursor.
The Cursor FOR loop construction avoids the need to declare everything explicitly.
create or replace procedure p1
( n in number )
as
begin
for r in (
select e_name, inv_amount
from employee e
join investment i on i.e_id = e.e_id
where inv_amount = n
)
loop
dbms_output.put_line(r.e_name || ': ' || r.inv_amount);
end loop;
end p1;
It's also best to write joins using the join keyword.
Note that SQL is the query language and PL/SQL is the programming language, so this a PL/SQL procedure, not SQL.
Regarding "Procedure created with compilation errors", you need to get familiar with how to display and trace compilation errors using your development tool. In SQL*Plus, type
show errors
and you'll get something like this (using your original version of the procedure):
Warning: Procedure created with compilation errors.
SQL> show errors
Errors for PROCEDURE P1:
LINE/COL ERROR
---------- ---------------------------------------------------------------------------------------------------
10/34 PLS-00112: end-of-line in quoted identifier
10/35 PLS-00103: Encountered the symbol "||'inv_amount');" when expecting one of the following:
( - + case mod new null <an identifier>
<a double-quoted delimited-identifier> <a bind variable>
continue avg count current max min prior sql stddev sum
variance execute forall merge standard time timestamp
interval date
<a string literal with character set specification>
<a number> <a single-quoted SQL string> pipe
<an alternatively-quoted string literal with character set specification>
<
You can use the list command to print individual lines, for example line 10 mentioned in the compilation error:
SQL> l 10
10* dbms_output.put_line('e_name'||"||'inv_amount');
or list a range, e.g. lines 8-11:
SQL> l 8 11
8 begin
9 open c1;
10 dbms_output.put_line('e_name'||"||'inv_amount');
11* loop
If you are not using SQL*Plus, you can always query the user_errors dictionary view. However, developer tools like SQL Developer, PL/SQL Developer and Toad highlight compilation errors right in the editor so no special commands are needed.

I am getting an error message in Oracle SQL stored procedure

I am trying the following code
CREATE OR REPLACE PROCEDURE sal2
AS
BEGIN
SELECT sal
FROM emp
END
And I'm getting this error
Error at line 7: PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
( begin case declare end exception exit for goto if loop mod
null pragma raise return select update while with
<< continue close current delete fetch lock
insert open rollback savepoint set sql execute commit forall
merge pipe purge
5. from emp
6. return 1
7. END
What I want to do is to get a sal column from emp table.
There are multiple issues with code as following;
create or replace procedure sal2
AS
Lv_var number; -- added variable for holding select value
BEGIN
select sal
Into lv_var -- into is needed in select
from emp; -- you missed this semicolon
-- you must use where clause as into variable can hold single value
-- so you must restrict this query to return only 1 record.
END; -- you missed this semicolon
/
Cheers!!
First you have to put the semicolon at the end of each line and after the END keyword. Then the SELECT statement is also wrong you have to load the result in a variable.
Here is the solution for multiple rows.
CREATE OR REPLACE PROCEDURE sal2
AS
CURSOR c_salaries IS SELECT salary FROM employees;
TYPE t_salaries IS TABLE OF c_salaries%rowtype INDEX BY BINARY_INTEGER;
v_salaries t_salaries;
BEGIN
OPEN c_salaries;
FETCH c_salaries BULK COLLECT INTO v_salaries;
CLOSE c_salaries;
END;
And one for a single row result.
CREATE OR REPLACE PROCEDURE sal2
AS
v_salary employees.salary%rowtype;
BEGIN
SELECT salary INTO v_salary
FROM employees WHERE employee_id = 100;
END;

Trying to assign a single value from a query to a variable... what am i doing wrong?

I am trying to to set a variable (v_flag_id) to the result of a query. I've been looking online at examples and it seems like my formatting/syntax is correct. What am i doing wrong? Thanks in advance.
create or replace PROCEDURE RUN_AGG
is
declare
v_Flag_id Number := select flag_id from flag where flag_tx = 'Processed / Calculated';
CURSOR hours IS
SELECT distinct(HR) as RHR
, submission_value_id
from (
select
v.DATA_DATE,
v.HR,
sv.submission_value_id
from value v
inner join submission_value sv on sv.value_id = v.value_id
where sv.SUBMISSION_VALUE_ID NOT IN (
SELECT SUBMISSION_VALUE_ID FROM VALUE_FLAG WHERE VALUE_FLAG.FLAG_ID = v_Flag_id
);
BEGIN
OPEN hours;
LOOP
FETCH hours into l_hr;
EXIT WHEN hours%NOTFOUND;
AGG_HOURLY_REG_FINAL(l_hr.RHR);
END LOOP;
CLOSE hours;
END RUN_AGG;
The error that I am receiving is as follows:
Error(6,1): PLS-00103: Encountered the symbol "DECLARE" when expecting one
of the following: begin function pragma procedure subtype type <an
identifier> <a double-quoted delimited-identifier> current cursor delete
exists prior external language
Use the following :
CREATE OR REPLACE PROCEDURE RUN_AGG IS
l_rhr VARCHAR2 (100);
l_sub_vl_id VARCHAR2 (100);
CURSOR hours is
SELECT distinct (HR) as RHR, submission_value_id
FROM (SELECT v.DATA_DATE, v.HR, sv.submission_value_id
FROM value_ v
INNER JOIN submission_value sv
ON (sv.value_id = v.value_id)
WHERE sv.SUBMISSION_VALUE_ID NOT IN
(SELECT SUBMISSION_VALUE_ID
FROM VALUE_FLAG
WHERE VALUE_FLAG.FLAG_ID in
(SELECT flag_id
FROM flag
WHERE flag_tx = 'Processed / Calculated')));
BEGIN
OPEN hours;
LOOP
FETCH hours INTO l_rhr, l_sub_vl_id;
EXIT WHEN hours%NOTFOUND;
AGG_HOURLY_REG_FINAL(l_rhr);
END LOOP;
CLOSE hours;
END RUN_AGG;
remove declare
take select flag_id into v_Flag_id from flag where flag_tx =
'Processed / Calculated'; sql in hours cursor's select. So, remove v_Flag_id variable.
return two variables for two columns l_rhr and l_sub_vl_id.
I replaced the name of the table value with value_, since it's a
reserved keyword for oracle.

Oracle PL/SQL Stored Procedure Cursor for update

I am trying to update the 'damage_amt' of cars by increasing the value by 'inc', but only for accidents that occurred in year 'year'.
CREATE or REPLACE PROCEDURE updateDMG(xyear VARCHAR2, inc NUMBER) AS
CURSOR cur1 IS
select a.DAMAGE_AMT
from participated a join accident b
on a.report_nr = b.report_nr
for update;
p_dmg PARTICPATED.damage_amt%TYPE;
p_year NUMBER;
inc_dmg NUMBER;
BEGIN
p_year:=xyear;
inc_dmg:=inc;
OPEN cur1;
LOOP
FETCH cur1 bulk collect INTO p_dmg;
EXIT WHEN cur1%NOTFOUND;
UPDATE PARTICIPATED
SET damage_amt = damage_amt * inc_dmg
WHERE p_dmg like xyear;
END LOOP;
CLOSE cur1;
END updateDMG;
/
EXEC updateDMG('08', 0.10);
But I'm getting the error:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: begin function pragma procedure subtype <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior
Obviously I am lacking knowledge on the syntax of this type of answer. Can anyone point out my error? I can't seem to find anything from searching.
EDIT: Was missing a / after END. Now it doesn't actually update the rows.
FINAL EDIT: Figured it out. I think some of my variables are unnecessary, but I could be wrong.
CREATE or REPLACE PROCEDURE updateDMG(xyear NUMBER, inc DECIMAL) AS
CURSOR cur1 IS
select a.DAMAGE_AMT, extract(year from b.ACCIDENT_DATE)
from participated a join accident b
on a.report_nr = b.report_nr
for update;
p_dmg PARTICIPATED.damage_amt%TYPE;
p_year NUMBER;
input_year NUMBER;
inc_dmg DECIMAL;
BEGIN
input_year:=xyear;
inc_dmg:=inc;
OPEN cur1;
FETCH cur1 INTO p_dmg, p_year;
MERGE INTO PARTICIPATED x
USING ACCIDENT y
ON (x.report_nr = y.report_nr)
WHEN MATCHED THEN
UPDATE SET x.damage_amt = x.damage_amt * (1 + inc_dmg/100)
WHERE extract(year from y.accident_date) = input_year;
CLOSE cur1;
END updateDMG;
/
EXEC updateDMG(2008, 10);
I don't have a database right now to fully check syntax, but I would suggest something like this:
CREATE OR REPLACE PROCEDURE updatedmg (p_year NUMBER, p_inc DECIMAL)
AS
l_next_year NUMBER;
l_from_date DATE;
l_to_date DATE;
BEGIN
l_next_year := p_year + 1;
l_from_date := TO_DATE (p_year || '-01-01', 'RRRR-MM-DD');
l_to_date := TO_DATE (l_next_year || '-01-01', 'RRRR-MM-DD');
UPDATE participated pt
SET pt.damage_amt = pt.damage_amt * (1 + p_inc / 100)
WHERE pd.report_nr IN (SELECT ad.report_nr
FROM accident ad
WHERE ad.accident_date >= l_from_date
AND ad.accident_date < l_to_date);
END updatedmg;
Its simple and that way you probably can make better use of indexes on tables.
I hope it helps.
Good luck!

Need an alternative solution to this oracle query?Without using (flag) variable

Question:
Create a Doctor table (Docname, Qualification, Specialization, Working_shift).
Use parameterized cursor to check the availability of doctors given the specialization
and working shift of the day to serve the patients
I am just learning databases so if the question may seem trivial i apologize for that.
Getting the desired output on inputting the values but i need an alternative way to solve the question without using flag variable (so that i could get the exception)...if i don't use the flag it prints the exception as well as the docname and qualification
I am using oracle(cursor in a normal pl/sql block) to execute this query.
Solution:
--table creation
create table doctor
(
docname varchar2(20),
qualification varchar2(20),
specialization varchar2(20),
shift varchar2(20)
)
my solution
declare
cursor c1 (specialization varchar2,shift varchar2) is select docname,qualification from doctor
where specialization='&sp' and shift='&shift'
sp doctor.specialization%type;
shift doctor.shift%type;
flag number(10);
begin
flag:=0;
for r1 in c1(sp,shift)
loop
if c1%found then
flag:=1;
dbms_output.put_line('Doctor is available');
dbms_output.put_line('Docname: '||r1.docname);
dbms_output.put_line('qualification: '||r1.qualification);
else
flag:=0;
end if;
end loop;
if flag=0 then
dbms_output.put_line('Invalid specialization/shift');
end if;
end;
Try given below code
declare
cursor c1 (specialization varchar2,shift varchar2)
is
select docname,qualification
from doctor
where specialization='&sp'
and shift='&shift'
sp doctor.specialization%type;
shift doctor.shift%type;
flag number(10);
begin
flag:=0;
for r1 in c1(sp,shift)
loop
if c1%found then
flag:=1;
dbms_output.put_line('Doctor is available');
dbms_output.put_line('Docname: '||r1.docname);
dbms_output.put_line('qualification: '||r1.qualification);
else
raise;
end if;
end loop;
exception
when others then
dbms_output.put_line('Invalid specialization/shift');
end;
You don't need to reset the flag within the loop, you already initialised it to 0 at the start of the procedure.
You dont need to check c1%found because you're inside the loop; by definition a record was found, otherwise it wouldn't go into your loop code.
Your cursor should use the variables provided, not the SQL*Plus substitution variables, e.g.:
cursor c1 (specialization varchar2,shift varchar2) is
select docname,qualification
from doctor
where doctor.specialization=c1.specialization
and doctor.shift=c1.shift;
If you don't want to have to use all those aliases, you can use a naming convention to distinguish between the different identifiers (shift vs shift), e.g.:
cursor c1 (i_specialization varchar2, i_shift varchar2) is
select docname,qualification
from doctor
where specialization=i_specialization
and shift=i_shift;
Note also, you missed a semicolon at the end of the query.
Finally:
If you change your loop as follows, it should work fine:
for r1 in c1(&sp,&shift)
loop
flag:=1;
dbms_output.put_line('Doctor is available');
dbms_output.put_line('Docname: '||r1.docname);
dbms_output.put_line('qualification: '||r1.qualification);
end loop;
Now, your last bit of code:
if flag=0 then
dbms_output.put_line('Invalid specialization/shift');
end if;
will work fine - it will only execute if flag is still 0 (i.e. the query found no rows).
If you do not use parameters in "c1" cursor you do not need it...
DECLARE
CURSOR c1 IS
SELECT docname, qualification
FROM doctor
WHERE specialization = '&sp'
AND shift = '&shift';
TYPE c1_ntt IS TABLE OF c1%ROWTYPE;
l_c1 c1_ntt;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO l_c1;
CLOSE c1;
IF l_c1.COUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Invalid specialization/shift');
END IF;
FOR indx IN l_c1.FIRST..l_c1.LAST LOOP
DBMS_OUTPUT.PUT_LINE('Doctor is available');
DBMS_OUTPUT.PUT_LINE('Docname: ' || l_c1(indx).docname);
DBMS_OUTPUT.PUT_LINE('qualification: ' || l_c1(indx).qualification);
END LOOP;
END;

Resources