We are supposed to read a text file, insert that data into a table while taking care of exceptions. This the code for my function:
set serveroutput ON;
CREATE OR replace FUNCTION Order_func(ldir VARCHAR2,
lfile VARCHAR2)
RETURN BOOLEAN
AS
result BOOLEAN;
f utl_file.file_type;
s VARCHAR2(200);
v_row VARCHAR2(2000);
v1 NUMBER;
v2 NUMBER;
v3 NUMBER;
v4 DATE;
v5 DATE;
v6 NUMBER;
v7 NUMBER;
v8 NUMBER;
v9 NUMBER;
customer_error EXCEPTION;
employee_error EXCEPTION;
item_error EXCEPTION;
customerids NUMBER;
employeeids NUMBER;
inventoryids NUMBER;
BEGIN
SELECT cno
INTO customerids
FROM customers;
SELECT employeeno
INTO employeeids
FROM employees;
SELECT itemno
INTO inventoryids
FROM inventory;
f := utl_file.Fopen(ldir, lfile, 'R');
LOOP
utl_file.Get_line(f, v_row);
v1 := Substr(v_row, 1, 4);
v2 := Substr(v_row, 6, 9);
v3 := Substr(v_row, 11, 12);
v4 := Substr(v_row, 15, 23);
v5 := Substr(v_row, 27, 35);
v6 := Substr(v_row, 38, 41);
v7 := Substr(v_row, 43);
v8 := Substr(v_row, 45, 48);
v9 := Substr(v_row, 50, 51);
IF v2 <> customerids THEN --checking customer id
RAISE customer_error;
ELSIF v3 <> employeeids THEN --checking employee id
RAISE employee_error;
ELSIF v6 <> inventoryids THEN --checking item1 id
RAISE item_error;
ELSIF v8 <> inventoryids THEN --checking item2 id
RAISE item_error;
ELSE
INSERT INTO transactions
(tid, orderno, cno, employeeno, received,
shipped, itemno1, quantity1, itemno2, quantity2)
VALUES (sequence_tid.NEXTVAL, v1, v2, v3, v4, v5,
v6, v7, v8, v9);
END IF;
END LOOP;
result := TRUE;
RETURN result;
EXCEPTION
WHEN customer_error THEN
dbms_output.Put_line('Customer not found in parent Customer table'); WHEN
employee_error THEN
dbms_output.Put_line('Employee not found in Employee table');
WHEN item_error THEN
dbms_output.Put_line('Item not found in inventory table'); WHEN OTHERS THEN
dbms_output.Put_line('Error code:'
|| SQLCODE
|| '. Error Message: '
|| SQLERRM);
utl_file.Fclose(f);
result := FALSE;
RETURN result;
END order_func;
This is how i've called the function (but i think it's wrong):
DECLARE
results boolean;
BEGIN
results := order_func('forQues','items.txt');
DBMS_OUTPUT.PUT_LINE('Result for ORDER_FUNC Function is: ' || results);
END;
And this is the error I've got:
DECLARE
results boolean;
BEGIN
results := order_func('forQues','items.txt');
DBMS_OUTPUT.PUT_LINE('Result for ORDER_FUNC Function is: ' || results);
END;
Error report -
ORA-06550: line 5, column 26:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 5, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I tried to remove the results in the dbms_output and this is what happened:
DECLARE
results boolean;
BEGIN
results := order_func('forQues','items.txt');
DBMS_OUTPUT.PUT_LINE('Result for ORDER_FUNC Function is:');
END;
After running this i got:
Error code:-1422. Error Message: ORA-01422: exact fetch returns more than requested number of rows
Result for ORDER_FUNC Function is:
PL/SQL procedure successfully completed.
Please somebody help, how to fix this?
In addition to the errors already pointed out you function has a couple issues, at least. The biggest problem is, I believe, your misunderstanding of the Substring function, in particular the 3rd parameter. The 3rd parameter optionally indicated the length desired not the ending position. For example the variable v5 is defined as a date but the value is set as "v5 := Substr(v_row, 27, 35);". Do you really have a date that is 35 characters long. Additionally when the 3rd parameter is omitted then the resulting value extends from the 2nd parameter to the end of the string. So v7 extends from position 43 to the end of string. V8, V9 then are numbers that are 48 and 51 digits respectively and every field overlaps the one following.
The other big issue is the error NO_DATA_FOUND. You have three places where this is likely to occur (the selects) and 1 where it will occur (utl_file.Get_line - that's how utl_file indicates end-of-file). As structured you have no way of knowing which initiated that error. In the following I propose a method to resolve this issue. I break each of those element into a theit own procedure. I leave the substring issue for you to resolve.
Before that a little philosophy. Names and classifications are important. Names should describe what the represent but V1, V2 ... do not; so instead of v5 perhaps v_shipped_dt. Likewise declaring a procedure vs. function should done based on purpose of the routine. Procedures accomplish things, fuunctions get things. It seems the purpose here is to load data not return boolean. So I made it a procedure (it reports back via an out parameter). (Normally I wouldn't even do that. It runs successfully and returns nothing or if fails raising an exception. But that's perhaps stylistic.)
create or replace procedure load_order_file
( ldir varchar2
, lfile varchar2
, result out varchar2
)
as
customer_error exception;
employee_error exception;
item_error exception;
f utl_file.file_type;
v_row varchar2(2000);
v1 number;
v2 number;
v3 number;
v4 date;
v5 date;
v6 number;
v7 number;
v8 number;
v9 number;
EOF_Reached Boolean ;
-- procedure to handle all file processing
procedure load_order_buffer
is
begin
if f is null or not util_file.is_open(f)
then
f := utl_file.fopen(ldir, lfile, 'R');
EOF_Reached := False;
end if ;
utl_file.get_line(f, v_row);
exception
when no_data_found then
EOF_Reached := True;
util_file.fclose(f);
end load_order_buffer;
-- procedure to split file line into local variables
procedure parse_buffer
is
begin
v1 := substr(v_row, 1, 4);
v2 := substr(v_row, 6, 9);
v3 := substr(v_row, 11, 12);
v4 := substr(v_row, 15, 23);
v5 := substr(v_row, 27, 35);
v6 := substr(v_row, 38, 41);
v7 := substr(v_row, 43);
v8 := substr(v_row, 45, 48);
v9 := substr(v_row, 50, 51);
end parse_buffer;
-- procedures to validate customer, employee, and inventory
procedure validate_customer(cust_no_in number)
is
l_exists varchar2(1);
begin
select null
into l_exists
from customers
where cno = cust_no_in;
exception
when no_data_found then
raise customer_error;
end validate_customer;
procedure validate_employee(employee_no_in number)
is
l_exists varchar2(1);
begin
select null
into l_exists
from employees
where employeeno = employee_no_in;
exception
when no_data_found then
raise employee_error;
end validate_employee;
procedure validate_inventory(inventory_no_in number)
is
l_exists varchar2(1);
begin
select null
into l_exists
from inventory
where itemno = inventory_no_in;
exception
when no_data_found then
raise item_error;
end validate_inventory;
-- Main
begin
-- set up initial assumptions;
result := 'Failed';
EOF_Reached := False;
loop
load_order_buffer;
exit when EOF_Reached;
parse_buffer;
validate_customer(v2);
validate_employee(v3);
validate_inventory(v6);
validate_inventory(v8);
-- everything valid create transaction
insert into transactions
(tid, orderno, cno, employeeno, received,
shipped, itemno1, quantity1, itemno2, quantity2)
values (sequence_tid.nextval, v1, v2, v3, v4, v5,
v6, v7, v8, v9);
end loop;
result := 'Success';
exception
when customer_error then
dbms_output.put_line('Customer not found in parent Customer table');
when employee_error then
dbms_output.put_line('Employee not found in Employee table');
when item_error then
dbms_output.put_line('Item not found in inventory table');
when others then
dbms_output.put_line('Error code:'
|| sqlcode
|| '. Error Message: '
|| sqlerrm);
end load_order_file ;
/
Here is a test driver
declare
result varchar2(8);
begin
load_order_file('forQues','items.txt');
dbms_output.put_line('Load Order file terminated with ' || result);
if result <> 'Success'
then
rollback;
end if ;
end ;
Since you provided neither table DDL nor sample data this is not tested . Good Luck.
Error ORA-01422: exact fetch returns more than requested number of rows
happens when your query returns multiple rows but you have an INTO
clause which only expects 1 row to be returned.
For example, your three queries:
select cno into customerids from customers;
select employeeno into employeeids from employees;
select itemno into inventoryids from inventory;
Error PLS-00306: wrong number or types of arguments in call to '||'
is thrown because you are trying to concatenate a string with a boolean at
DBMS_OUTPUT.PUT_LINE('Result for ORDER_FUNC Function is: ' || results);
which is not allowed.
Possible fixes for ORA-01422
error: Put your SELECT
in a FOR
loop like:
FOR c IN (
SELECT
cno
INTO customerids
FROM
customers
) LOOP
--do stuff, access column value like c.cno
END LOOP;
Possible fixes for PLS-00306
error: Change the concatenation to
DBMS_OUTPUT.PUT_LINE('Result for ORDER_FUNC Function is: ' || case when results then 'true' else 'false' end);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.