Need help with code and error. After executing this code:
CREATE OR REPLACE DIRECTORY dir_vezba AS '/home/oracle/vezba';
CREATE OR REPLACE PROCEDURE raise_salary
(p_deptno IN NUMBER, p_per IN NUMBER)
...
END raise_salary;
/
SET VERIFY OFF
DECLARE
v_deptno NUMBER;
v_per NUMBER;
v_oldsalary employees.salary%TYPE;
v_newsalary employees.salary%TYPE;
file_handle UTL_FILE.FILE_TYPE;
f_handle UTL_FILE.FILE_TYPE;
file_line VARCHAR2(200);
f_body VARCHAR2(200);
f_line VARCHAR2(200);
f_head VARCHAR2(200);
file_report VARCHAR2(150);
CURSOR emp_cursor IS
SELECT employee_id, salary
FROM employees
WHERE department_id=v_deptno;
BEGIN
f_handle:=UTL_FILE.FOPEN('DIR_VEZBA','POVECANJE_DEP.txt','r');
LOOP
BEGIN
UTL_FILE.GET_LINE(f_handle, file_line);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
v_deptno:=TO_NUMBER(SUBSTR(file_line,1,3));
v_per:=TO_NUMBER(SUBSTR(file_line,4,3))/1000;
file_report:='IZVESTAJ'||TO_CHAR(SYSDATE, 'YY')||TO_CHAR(v_deptno)||'.log';
file_handle:=UTL_FILE.FOPEN('DIR_VEZBA', file_report, 'w');
UTL_FILE.PUTF(file_handle, 'Report generated on: '||SYSDATE||' for department: '||v_deptno);
UTL_FILE.NEW_LINE(file_handle);
UTL_FILE.NEW_LINE(file_handle);
f_head:='EMPNO OLD_SALARY NEW_SALARY';
UTL_FILE.PUTF(file_handle, f_head);
UTL_FILE.NEW_LINE(file_handle);
f_line:='=================================';
UTL_FILE.PUTF(file_handle, f_line);
UTL_FILE.NEW_LINE(file_handle);
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary(v_deptno, v_per);
SELECT salary
INTO v_newsalary
FROM employees
WHERE employee_id=emp_rec.employee_id;
f_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(file_handle, f_body);
UTL_FILE.FCLOSE(file_handle);
END LOOP;
END LOOP;
UTL_FILE.FCLOSE(f_handle);
EXCEPTION
WHEN UTL_FILE.INVALID_OPERATION THEN
file_report:='IZVESTAJ'||TO_CHAR(SYSDATE, 'YY')||'.bad';
file_handle:=UTL_FILE.FOPEN('DIR_VEZBA', file_report, 'w');
UTL_FILE.PUTF(file_handle, 'NO FILE FOUND');
UTL_FILE.FCLOSE(file_handle);
END;
/
I get this error:
ORA-29282: invalid file ID
ORA-06512: at "SYS.UTL_FILE", line 1071
ORA-06512: at line 48
Don't know what's the problem with this and what does it means. Besides that, in output I get only one line txt file and nothing else. It should get me 3 text fles with different number of lines in them, depending on the data in tables.
This is your problem:
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary(v_deptno, v_per);
SELECT salary
INTO v_newsalary
FROM employees
WHERE employee_id=emp_rec.employee_id;
f_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(file_handle, f_body);
----------> UTL_FILE.FCLOSE(file_handle); <-----------
END LOOP;
You close the file while in the loop, so at the next iteration the PUTF will fail.
It has been my experience that the directory name must be in ALL CAPS, but the filename must be all lower case. I know it sounds stupid, and it probably is, but if no other answer has solved your problem, what have you got to lose by trying this?
Related
I have written following code in oracle pl/sql
create or replace procedure sorting_criteria(criteria in varchar)
as
begin
if(criteria='lowest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount ASC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else if(criteria='highest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount DESC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end;
/
But it is giving following error: Error at line 35: PLS-00103: Encountered the symbol ";" when expecting one of the following: Please help
The ELSE-IF statement in PL/SQL has to be written as ELSIF. Otherwise, you should close the second IF with an other END IF; statement.
You can solve the issue by changing the ELSE IF at line 17 to an ELSIF
The answer by #GregorioPalamà correctly addresses your issues. But you can drastically reduce the workload by changing your thinking away from "If...then...else" to the "set of" and letting SQL do the work. In this case the only difference is sorting either ascending or descending on amount. The same effect can be achieved by sorting ascending on amount or minus amount; and SQL can make that decision. So you can reduce the procedure to validating the parameter and a single cursor for loop:
create or replace procedure sorting_criteria(criteria in varchar2)
as
cursor ptr(c_sort_criteria varchar2) is
select p_name
from product
order by case when c_sort_criteria = 'lowest price'
then amount
else -amount
end ;
begin
if criteria in ('lowest price', 'highest price')
then
for rec in ptr(criteria)
loop
dbms_output.put_line('Product: ' || rec.p_name );
end loop;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end sorting_criteria;
/
See demo here. For demonstration purposed I added the amount to the dbms_output.
A couple notes:
While it is not incorrect using p_... as a column name, it is also
not a good idea. A very common convention (perhaps almost a
standard) to use p_... to indicate parameters. This easily leads to
confusion; confusion amongst developers is a bad thing.
IMHO it is a bug to name a local variable the same as a table
column name. While the compiler has scoping rules which one to use
it again leads to confusion. The statement "where table.name = name"
is always true, except when at least one of them is null, which possible could lead to updating/deleting every row in your table. In this
case p_name is both a column and a local variable.
i have a PROCEDURE like this, whenever i execute the procedure it will give result in cursor according select type:
create or replace PROCEDURE EMAT_PROC_DROPDOWN_SELECTALL(SELECT_TYPE IN VARCHAR2, RESULT_SET OUT SYS_REFCURSOR)
AS
SELECT_TYPE_IN VARCHAR2(100);
TABLE_NAME VARCHAR2(100);
FIELD_NAME VARCHAR2(100);
C_ZONE SYS_REFCURSOR;
C_USER_ID SYS_REFCURSOR;
BEGIN
SELECT_TYPE_IN:=SELECT_TYPE;
IF(SELECT_TYPE_IN ='ZONE') THEN
BEGIN
TABLE_NAME:='MM_ZONEMASTER';
FIELD_NAME:='ZONE_NAME';
OPEN C_ZONE FOR
'SELECT DISTINCT' ||FIELD_NAME|| 'FROM' ||TABLE_NAME||';';
LOOP
FETCH C_ZONE INTO RESULT_SET;
EXIT WHEN C_ZONE%NOTFOUND;
END LOOP;
CLOSE C_ZONE;
END;
ELSIF(SELECT_TYPE_IN ='USER') THEN
BEGIN
TABLE_NAME:='AD_USERMASTER';
FIELD_NAME:='USER_ID';
OPEN C_USER_ID FOR
'SELECT DISTINCT' ||FIELD_NAME|| 'FROM' ||TABLE_NAME||';';
LOOP
FETCH C_USER_ID INTO RESULT_SET;
EXIT WHEN C_USER_ID%NOTFOUND;
END LOOP;
CLOSE C_ZONE;
END;
END IF;
END;
but the returns the error ***ORA-00972: identifier is too long
ORA-06512: at "EMAT.EMAT_PROC_DROPDOWN_SELECTALL", line 14
ORA-06512: at line 7
This is the issue: 'SELECT DISTINCT' ||FIELD_NAME|| 'FROM' ||TABLE_NAME||';';
It will see it as SELECT DISTINCTZONE_NAMEFROMMM_ZONEMASTER
The same goes for the other one.
Add spaces and write it as SELECT DISTINCT ' ||FIELD_NAME|| ' FROM ' ||TABLE_NAME||';';
There are missing space in the cursor around the field name and the table name. It reads SELECT DISTINCTZONE_NAMEFROMMM_ZONEMASTER; instead of SELECT DISTINCT ZONE_NAME FROM MM_ZONEMASTER;
Replace that line by this should help :
'SELECT DISTINCT ' ||FIELD_NAME|| ' FROM ' ||TABLE_NAME||';';
Additionally be careful with the use of nested blocks in your IF. I would avoid abusing from it if it's not needed.
My whole intention to catch exception,WRONG parameter is NOT CATCHING exception.
Here is the code:
CREATE OR REPLACE PROCEDURE list_emp (p_emp_id IN employees.employee_id%TYPE,
p_dept_id IN employees.department_id%TYPE)
IS
CURSOR c1 IS
SELECT *
FROM EMPLOYEES
WHERE EMPLOYEE_ID=p_emp_id
AND DEPARTMENT_ID=p_dept_id;
emp_rec c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
END LOOP;
CLOSE c1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
END;
When the cursor is opened and fetched with the wrong parameter that does not match any row from the corresponding table, the following line
EXIT WHEN c1%NOTFOUND;
cause the plsql procedure to terminate (because there were no rows found). Hence no exception is raised.
If you do want to display some sort of output you can do the following instead
IF c1%FOUND THEN
dbms_output.put_line('Record Found');
ELSE
dbms_output.put_line('Finished/Done');
EXIT;
END IF;
If you want to raise an error after looping through a cursor that returns no rows, then you're going to have to use a counter to work out how many rows have been processed, and then you can do something if no rows have been processed.
Something like:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
cursor c1 is
select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
v_count number := 0;
begin
for emp_rec in c1
loop
v_count := v_count + 1;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
end loop;
if v_count = 0 then
raise no_data_found;
end if;
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
A few notes:
I converted your cursor loop into a cursor-for-loop; you don't need to worry about declaring the record type and also Oracle handles the opening and closing of the cursor for you.
I added raise; to each of your exception handlers - in general, having when others then null (which is effectively what your original code was doing - no errors are raised to the calling code) is a bad idea. I added the raise to the no_data_found condition as that wasn't doing anything either; typically, if you have an exception condition, you want it to do something to let the calling code know there was a problem (not always, of course; sometimes you don't want the processing to stop if a particular error condition is met).
Your cursor was selecting all columns, but in your procedure, you were only using three of them. I've therefore amended the cursor so that it only pulls back those three columns.
Don't rely on dbms_output in your production code. Code that calls this procedure won't see anything populated in dbms_output, unless it explicitly looks for it - and that's not something I've ever seen in any production code, outside of Database tools (eg. SQL*Plus, Toad, etc). I've left this in your procedure as I've a feeling this is a learning exercise for you, but please don't think that this is in any way acceptable in production code.
You're passing p_emp_id in as a parameter - typically, that's the primary key of the employees table. If that's the case, then there's no need for the cursor for loop at all - you could do it by using select ... into ... instead, like so:
.
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
v_emp_id employees.employee_id%type;
v_first_name employees.first_name%type;
v_last_name employees.last_name%type;
begin
select employee_id,
first_name,
last_name
into v_emp_id,
v_first_name,
v_last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
Alternatively, just pass back a ref cursor:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type,
p_ref_cur out sys_refcursor)
is
begin
open p_ref_cur for select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
-- No need for an exception handler here since you're not storing the error details anyway.
-- By not having an error handler, any error will automatically be raised up to the calling code
-- and it will have the correct error stack trace info (e.g. the line number the error occurred,
-- rather than the line the error was reraised from
end;
/
And to run the ref cursor in SQL*Plus (or as a script in Toad/SQL Developer/etc), you do the following:
-- create a variable outside of PL/SQL to hold the ref cursor pointer (this is a SQL*Plus command):
variable rc refcursor;
-- populate our ref cursor variable with the pointer. Note how we pass it in as a bind variable
begin
list_emp(p_emp_id => 1234,
p_dept_id => 10,
p_ref_cur => :rc);
end;
/
-- finally, print the contents of the ref cursor.
print rc;
I have to write program that generates file, which lines are of the next content:
EMPLOYEE_ID, OLD_SALARY, NEW_SALARY
For that I wrote a procedure that raises salaries for a given department, and program that generates that file. Now, my procedure is working just fine and when I compile a program it doesn't have any errors. But the problem is that it works wrong. When I execute it, I get this
108 12008 12008
109 9000 9000
110 8200 8200
111 7700 7700
112 7800 7800
113 6900 6900
OLD_SALARY column and NEW_SALARY column are same, and it didn't raise them at all. After second executing, columns are still same but the salaries are like 4 time after first executing.
I think the problem is in my cursor but I don't know how to solve it. Here is the entire code:
CREATE OR REPLACE DIRECTORY dir_vezba AS '/home/oracle/vezba';
CREATE OR REPLACE PROCEDURE raise_salary
IS
v_deptno NUMBER:=100;
v_per NUMBER:=0.2;
BEGIN
UPDATE employees
SET salary=salary*(1+v_per)
WHERE department_id=v_deptno;
UPDATE employees
SET salary=salary*1.05
WHERE department_id=v_deptno AND employee_id IN (SELECT e.employee_id
FROM employees e JOIN employees m
ON (e.manager_id=m.employee_id));
END raise_salary;
/
SET VERIFY OFF
DECLARE
v_deptno NUMBER;
v_oldsalary employees.salary%TYPE;
v_newsalary employees.salary%TYPE;
v_filehandle UTL_FILE.FILE_TYPE;
v_body VARCHAR2(200);
v_line VARCHAR2(200);
v_head VARCHAR2(200);
v_report VARCHAR2(150);
CURSOR emp_cursor IS
SELECT employee_id, salary
FROM employees
WHERE department_id=v_deptno;
BEGIN
v_deptno:=extract_dep('POVECANJE_DEP.txt');
v_report:='IZVESTAJ'||TO_CHAR(SYSDATE, 'YY')||TO_CHAR(v_deptno)||'.log';
v_filehandle:=UTL_FILE.FOPEN('DIR_VEZBA', v_report, 'w');
UTL_FILE.PUTF(v_filehandle, 'Report generated on: '||SYSDATE||' for department: '||v_deptno);
UTL_FILE.NEW_LINE(v_filehandle);
UTL_FILE.NEW_LINE(v_filehandle);
v_head:='EMPNO OLD_SALARY NEW_SALARY';
UTL_FILE.PUTF(v_filehandle, v_head);
UTL_FILE.NEW_LINE(v_filehandle);
v_line:='=================================';
UTL_FILE.PUTF(v_filehandle, v_line);
UTL_FILE.NEW_LINE(v_filehandle);
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary;
v_newsalary:=emp_rec.salary;
v_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(v_filehandle, v_body);
UTL_FILE.NEW_LINE(v_filehandle);
END LOOP;
UTL_FILE.FCLOSE(v_filehandle);
END;
/
The values in emp_rec are fetched before you call raise_salary, and won't be affected by the UPDATEs in raise_salary. To get the new value for the salary you'll need to fetch it from the database again. Try changing the main loop as follows:
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary;
SELECT SALARY
INTO v_newsalary
FROM EMPLOYEES
WHERE EMPLOYEE_ID = emprec.EMPLOYEE_ID;
v_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(v_filehandle, v_body);
UTL_FILE.NEW_LINE(v_filehandle);
END LOOP;
Share and enjoy.
Am getting this error 'PL/SQL: ORA-00942: table or view does not exist' in Oracle 11G when I try to runt his portion of my script. It seems the select statement isn't parsing the name of the variable from the cursor. Need help to ensure this can read the table name variable from the cursor.
DECLARE
ITEM_ERROR NUMBER;
CNT_SITE VARCHAR2(46);
CURSOR C_SITEID IS
SELECT OBJECTNAME,ATTRIBUTENAME FROM MAXATTRIBUTE WHERE ATTRIBUTENAME LIKE 'SITE%' GROUP BY OBJECTNAME,ATTRIBUTENAME, COLUMNNAME;
SITEIDRec C_SITEID%RowType;
BEGIN
BEGIN
OPEN C_SITEID;
LOOP
FETCH C_SITEID into SITEIDRec;
EXIT WHEN C_SITEID %NOTFOUND;
BEGIN
SELECT COUNT(SITEID) into CNT_SITE FROM SITEIDRec.OBJECTNAME
WHERE SITEID IN ('COLLEGE NANO SCI ENG-TGCM','FREESCALE-BALAZS','TGCM-GLOBAL FOUNDRIES','INTL RECTIFIER-TGM','TGCM-DMOS5','TGCM-IMFT','TGCM-TRIQUINT','GP-TRIQUINT');
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
END;
END LOOP;
--COMMIT;
CLOSE C_SITEID;
--COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
END;
--COMMIT;
EXCEPTION
WHEN OTHERS THEN
MSG_TEXT := SUBSTR(SQLERRM,1,200);
--COMMIT;
raise;
END;
I think you have FROM TableName missing in your query and hence it's rightly complaining.
SELECT COUNT(SITEID) into SITEIDRec.OBJECTNAME
WHERE SITEID IN
('COLLEGE NANO SCI ENG-TGCM','FREESCALE-BALAZS',
'TGCM-GLOBAL FOUNDRIES','INTL RECTIFIER-TGM','TGCM-DMOS5',
'TGCM-IMFT','TGCM-TRIQUINT','GP-TRIQUINT');
Please correct your query by adding the From TableName.
EDIT: Try using EXECUTE IMMEDIATE as below
EXECUTE IMMEDIATE 'SELECT COUNT(SITEID) into CNT_SITE FROM '
|| SITEIDRec.OBJECTNAME ||
' WHERE SITEID IN (''COLLEGE NANO SCI ENG-TGCM'',''FREESCALE-BALAZS'',
''TGCM-GLOBAL FOUNDRIES'',''INTL RECTIFIER-TGM'',''TGCM-DMOS5'',
''TGCM-IMFT'',''TGCM-TRIQUINT'',''GP-TRIQUINT'')';