I'm trying to make a procedure in pl/sql but I can't. I have 2 tables, one has 100 names in it and the other has 100 surnames. I want to make a procedure that takes a random name and surname, and then I want to introduce that data into another table, but I get an error and I'm new to pl/sql and don't know what to do. Here is my code.
DROP TABLE grade
/
CREATE TABLE grade (grade CHAR(2))
/
INSERT INTO grade VALUES ('A1');
INSERT INTO grade VALUES ('A2');
INSERT INTO grade VALUES ('A3');
INSERT INTO grade VALUES ('A4');
INSERT INTO grade VALUES ('A5');
INSERT INTO grade VALUES ('A6');
INSERT INTO grade VALUES ('A7');
INSERT INTO grade VALUES ('B1');
INSERT INTO grade VALUES ('B2');
INSERT INTO grade VALUES ('B3');
INSERT INTO grade VALUES ('B4');
INSERT INTO grade VALUES ('B5');
INSERT INTO grade VALUES ('B6');
INSERT INTO grade VALUES ('B7');
commit;
set serveroutput on
DECLARE
v_contor INTEGER := 1;
v_nr NUMBER(10);
v_name VARCHAR2(20);
v_surname VARCHAR2(20);
v_year NUMBER(2);
v_grade VARCHAR2(20);
v_scholarship NUMBER(4);
v_nota NUMBER(2);
PROCEDURE insert_data IS
cursor c1 is
select name from(select name from name
order by dbms_ryeardom.value)
where rownum =1;
cursor c2 is
select surname from(select surname from surname
order by dbms_ryeardom.value)
where rownum =1;
cursor c3 is
select grade from(select grade from grade
order by dbms_ryeardom.value)
where rownum =1;
BEGIN
v_nr := 124;
WHILE v_contor <= 2000 LOOP
open c1;
open c2;
open c3;
LOOP
fetch c1 into v_name;
exit when c1%notfound;
END loop;
LOOP
fetch c2 into v_surname;
exit when c2%notfound;
END loop;
LOOP
fetch c3 into v_grade;
exit when c3%notfound;
END loop;
v_year := round(dbms_ryeardom.value(1,3));
v_scholarship := round(dbms_ryeardom.value(250,450));
INSERT INTO studenti VALUES (v_nr, INITCAP(v_name), v_surname, v_year, v_grade, v_scholarship, NULL);
IF (v_year = 2) THEN
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 21, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 22, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 23, v_nota, NULL);
END if;
IF (v_year = 3) THEN
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 21, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 22, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 23, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 24, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 25, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 26, v_nota, NULL);
v_nota := round(dbms_ryeardom.value(4,10));
INSERT INTO note VALUES (v_nr, 29, v_nota, NULL);
END if;
v_nr := v_nr + 1;
v_contor := v_contor + 1;
close c1;
close c2;
close c3;
END loop;
END;
/
BEGIN
insert_data;
END;
/
You are declaring the procedure in a PL/SQL anonymous block and not in the SQL - this is not a problem but you are completing the declaration of the procedure and are not completing the declaration of the anonymous block.
An anonymous PL/SQL block has the format:
DECLARE
...
BEGIN
...
END;
/
The PROCEDURE declaration goes between the DECLARE and BEGIN clauses and has the form:
PROCEDURE name IS ... BEGIN ... END;
There is no trailing / at the end of the procedure declaration as ; terminates a PL/SQL statement and / is used to terminate the outer-most PL/SQL block (indicating that you are returning to the SQL scope - and if you were nesting PL/SQL blocks then you would not use the '/' terminator on the inner blocks).
If you remove the first / from your code then it should get rid of the end-of-file error.
You should also declare the variables used in the procedure within the procedure and not in the containing PL/SQL anonymous block (i.e. between the procedure's IS and BEGIN clauses and not between the outer blocks DECLARE and the PROCEDURE statements).
If you want to declare the procedure in the SQL scope (and not in an anonymous block) then you need to use:
CREATE [OR REPLACE] PROCEDURE name IS ... BEGIN ... END;
/
(The trailing / will be required in this instance as the statement will not be contained in a surrounding PL/SQL anonymous block and will be declared directly in the SQL scope so needs the / to indicate that the declaration of the procedure has concluded.)
You also have an issue that your cursors contain the order by dbms_ryeardom.value which appears to be a typo for order by dbms_random.value.
There may also be an issue with repeatedly opening and closing the same cursors.
First:
Create or replace procedure AS
not
procedure IS
Second:
U cannot declare
v_contor INTEGER := 1;
v_nr NUMBER(10);
v_name VARCHAR2(20);
v_surname VARCHAR2(20);
v_year NUMBER(2);
v_grade VARCHAR2(20);
v_scholarship NUMBER(4);
v_nota NUMBER(2);
and then creatin procedure. This declarations have to be inside procedure after 'AS'. I don't remember if in procedure u type lenght of value or not
f.e. v_r number; instead of v_r number(10);
try both ways:)
summa summarum - You procedure should start like this
create or replace PROCEDURE insert_data AS
v_grade VARCHAR2;
v_scholarship NUMBER;
v_nota NUMBER;
....
and about the end:
END;
/
BEGIN
insert_data;
END;
/
change to
END;
/
BEGIN
execute(insert_data);
END;
/
Related
I'm trying to return the number of rows per invoice_id using a function and procedure. Some invoice_id's have more than one row and I'm not sure how to fetch the count when I execute my procedure. As an example invoice_id(7) has just one row, but invoice_id(100) has four rows of information.
Create or replace function return_num_rows_function(invoice_id_text in varchar2)
Return varchar2
Is inv_id varchar2(20);
Begin
Select count(*)invoice_id into inv_id from invoice_line_items where invoice_id=invoice_id_text;
Return inv_id;
End;
Create or replace procedure return_num_rows (invoice_id_text in varchar2)
Is inv_id varchar(20);
line_item_desc invoice_line_items.line_item_description%type;
Begin
inv_id := return_num_rows_function(invoice_id_text);
If inv_id is not null then
Select count(*)invoice_id, line_item_description into inv_id,line_item_desc
From invoice_line_items where invoice_id = inv_id;
dbms_output.put_line('The number of rows returned:'|| inv_id);
dbms_output.put_line('Item description(s):'|| line_item_desc);
End if;
End;
set serveroutput on;
execute return_num_rows(7);
First of all do not use a string type variable for a numeric one
(invoice_id_text).
For your case it's better to use a procedure instead of called
function ( return_num_rows_function ), since you need two out
arguments returned.
A SQL Select statement cannot be used without Group By with aggegated and non-aggregated columns together ( i.e. don't use this one :
Select count(*) invoice_id, line_item_description
into inv_id,line_item_desc
From invoice_line_items
Where invoice_id = inv_id;
)
So, Try to create below procedures :
SQL> CREATE OR REPLACE Procedure
return_num_rows_proc(
i_invoice_id invoice_line_items.invoice_id%type,
inv_id out pls_integer,
line_item_desc out invoice_line_items.line_item_description%type
) Is
Begin
for c in
(
Select line_item_description
into line_item_desc
From invoice_line_items
Where invoice_id = i_invoice_id
)
loop
line_item_desc := line_item_desc||' '||c.line_item_description;
inv_id := nvl(inv_id,0) + 1;
end loop;
End;
/
SQL> CREATE OR REPLACE Procedure
return_num_rows(
i_invoice_id pls_integer
) Is
inv_id pls_integer;
line_item_desc invoice_line_items.line_item_description%type;
Begin
return_num_rows_proc(i_invoice_id,inv_id,line_item_desc);
If inv_id is not null then
dbms_output.put_line('The number of rows returned:' || inv_id);
dbms_output.put_line('Item description(s):' || line_item_desc);
End if;
End;
/
and call as in your case :
SQL> set serveroutput on;
SQL> execute return_num_rows(7);
Replace inv_id varchar2(20) with inv_id number;
and also if you want to get two outputs from procedure better to use refcursor.
Could you help me to pass the input values (at execution time: i mean to enter multiple values for single variable at once).
Here is my code for which i am giving one input at a time either hard coded input or single input at time.
declare
type TEmpRec is record (
EmployeeID EMPLOYEES.EMPLOYEE_ID%TYPE,
LastName EMPLOYEES.LAST_NAME%TYPE
);
type TEmpList is table of TEmpRec;
vEmpList TEmpList;
---------
function EmpRec(pEmployeeID EMPLOYEES.EMPLOYEE_ID%TYPE,
pLastName EMPLOYEES.LAST_NAME%TYPE default null) return TEmpRec is
-- Effective "Record constructor"
vResult TEmpRec;
begin
vResult.EmployeeID := pEmployeeID;
vResult.LastName := pLastName;
return vResult;
end;
---------
procedure SearchRecs(pEmpList in out nocopy TEmpList) is -- Nocopy is a hint to pass by reference (pointer, so small) rather than value (actual contents, so big)
vIndex PLS_integer;
begin
if pEmpList is not null then
vIndex := pEmpList.First;
while vIndex is not null -- The "while" approach can be used on sparse collections (where items have been deleted)
loop
begin
select LAST_NAME
into pEmpList(vIndex).LastName
from EMPLOYEES
where EMPLOYEE_ID = pEmpList(vIndex).EmployeeID;
exception
when NO_DATA_FOUND then
pEmpList(vIndex).LastName := 'F'||pEmpList(vIndex).EmployeeID;
end;
vIndex := pEmpList.Next(vIndex);
end loop;
end if;
end;
---------
procedure OutputRecs(pEmpList TEmpList) is
vIndex PLS_integer;
begin
if pEmpList is not null then
vIndex := pEmpList.First;
while vIndex is not null
loop
DBMS_OUTPUT.PUT_LINE ( 'pEmpList(' || vIndex ||') = '|| pEmpList(vIndex).EmployeeID||', '|| pEmpList(vIndex).LastName);
vIndex := pEmpList.Next(vIndex);
end loop;
end if;
end;
begin
vEmpList := TEmpList(EmpRec(100),
EmpRec( 34),
EmpRec(104),
EmpRec(110));
SearchRecs(vEmpList);
OutputRecs(vEmpList);
end;
/
Above program takes input value one at time.
However, i tried as below but unable to succeed.
i tried to give input from console at once like (100,34,104,100) in place of either hard coding the input (or) giving one input at time.
Snippet in DECLARE section:
declare
type TEmpRec is record (
EmployeeID EMPLOYEES.EMPLOYEE_ID%TYPE,
LastName EMPLOYEES.LAST_NAME%TYPE
);
type TEmpList is table of TEmpRec;
v_input TEmpList := TEmpList(&v_input); -- to read multiple input at once
vEmpList TEmpList;
In the final BEGIN section:
BEGIN
FOR j IN v_input.FIRST .. v_input.LAST LOOP
vEmpList := TEmpList(EmpRec(v_input(j).EmployeeID)); --to assign input values to vEmptList
SearchRecs(vEmpList);
OutputRecs(vEmpList);
end loop;
end;
/
Error in DECLARE section:
PLS-00306: wrong number or types of arguments in call to 'TEMPLIST'
Error in LAST BEGIN section:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
As an example: at time, i am able to read multiple input values for same variable but i am unable to pass this as an input but unable to figure out how can make this as an input my main program.
DECLARE
TYPE t IS TABLE OF VARCHAR2(100);
ORDERS t := t(&ORDERS);
BEGIN
FOR j IN ORDERS.FIRST .. ORDERS.LAST LOOP
dbms_output.put_line(ORDERS(j));
END LOOP;
END;
/
Output:
PL/SQL procedure successfully completed.
Enter value for orders: 321,153,678
321
153
678
Thank You.
Since You have a collection of record variable, you need to pass employee_ids and employee last_names separately. How are you planning to pass them in a single shot?.
Here is a sample script which accomplishes something you want with 2 inputs for 3 collection elements.
First, create a collection TYPE and a PIPELINED function to convert comma separated values into Collections - f_convert2.
CREATE TYPE test_type AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE FUNCTION f_convert2(p_list IN VARCHAR2)
RETURN test_type
PIPELINED
AS
l_string LONG := p_list || ',';
l_comma_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
BEGIN
LOOP
l_comma_index := INSTR(l_string, ',', l_index);
EXIT WHEN l_comma_index = 0;
PIPE ROW ( SUBSTR(l_string, l_index, l_comma_index - l_index) );
l_index := l_comma_index + 1;
END LOOP;
RETURN;
END f_convert2;
/
Then in your anonymous blocks pass values for employee_ids and last_name separately.
SET SERVEROUTPUT ON
DECLARE
TYPE temprec IS RECORD ( employeeid employees.employee_id%TYPE,
lastname employees.last_name%TYPE );
TYPE templist IS
TABLE OF temprec;
vemplist templist;
v_no_of_rec NUMBER := 10;
v_empl_ids VARCHAR2(100) := '&empl_ids';
v_empl_lnames VARCHAR2(100) := '&empl_lnames';
BEGIN
SELECT employee_id,last_name
BULK COLLECT
INTO
vemplist
FROM
(
SELECT
ROWNUM rn,
column_value employee_id
FROM
TABLE ( f_convert2(v_empl_ids) )
) a
JOIN (
SELECT
ROWNUM rn,
column_value last_name
FROM
TABLE ( f_convert2(v_empl_lnames) )
) b ON a.rn = b.rn;
FOR i in 1..vemplist.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(vemplist(i).employeeid || ' ' ||vemplist(i).lastname);
END LOOP;
END;
/
Instead of simple JOIN above if you use OUTER JOIN ( FULL or LEFT ), you can handle missing values without writing logic to check each value.
I am using Oracle 12, and I want to make a dynamic procedure which selects rows from specific table but according to an unknown conditio. That condition will be specified as input parameter.
Suppose I have a column called employee id and I want to call the procedure
with the following condition
execute s('employeeid = 2')
My code is
create or replace procedure s (condition varchar)
as
TYPE EmpCurTyp IS REF CURSOR; -- define weak REF CURSOR type
emp_cv EmpCurTyp; -- declare cursor variable
my_ename VARCHAR2(15);
my_sal NUMBER := 2;
mycondition varchar2(100):=condition;
BEGIN
OPEN emp_cv FOR -- open cursor variable
'SELECT employeeid, employeename FROM employees WHERE = :s' USING mycondition;
END;
but I am getting an error
missing expression
What am I doing wrong, and will the result of this procedure be selected rows from employees table that satisfy applied condition ?
The USING is meant to handle values, not pieces of code; if you need to edit your query depending on an input parameter ( and I believe this is a very dangerous way of coding), you should treat the condition as a string to concatenate to the query.
For example, say you have this table:
create table someTable(column1 number)
This procedure does somthing similar to what you need:
create or replace procedure testDyn( condition IN varchar2) is
cur sys_refcursor;
begin
open cur for 'select column1 from sometable where ' || condition;
/* your code */
end;
Hot it works:
SQL> exec testDyn('column1 is null');
PL/SQL procedure successfully completed.
SQL> exec testDyn('column99 is null');
BEGIN testDyn('column99 is null'); END;
*
ERROR at line 1:
ORA-00904: "COLUMN99": invalid identifier
ORA-06512: at "ALEK.TESTDYN", line 4
ORA-06512: at line 1
This is not embedded in a procedure yet but I tested this and works:
DECLARE
TYPE OUT_TYPE IS TABLE OF VARCHAR2 (20)
INDEX BY BINARY_INTEGER;
l_cursor INTEGER;
l_fetched_rows INTEGER;
l_sql_string VARCHAR2 (250);
l_where_clause VARCHAR2 (100);
l_employeeid VARCHAR2 (20);
l_employeename VARCHAR2 (20);
l_result INTEGER;
o_employeeid OUT_TYPE;
o_employeename OUT_TYPE;
BEGIN
l_cursor := DBMS_SQL.OPEN_CURSOR;
l_sql_string := 'SELECT employeeid, employeename FROM employees WHERE ';
l_where_clause := 'employeeid = 2';
l_sql_string := l_sql_string || l_where_clause;
DBMS_SQL.PARSE (l_cursor, l_sql_string, DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
1,
l_employeeid,
20);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
2,
l_employeename,
20);
l_fetched_rows := 0;
l_result := DBMS_SQL.EXECUTE_AND_FETCH (l_cursor);
LOOP
EXIT WHEN l_result = 0;
DBMS_SQL.COLUMN_VALUE (l_cursor, 1, l_employeeid);
DBMS_SQL.COLUMN_VALUE (l_cursor, 2, l_employeename);
l_fetched_rows := l_fetched_rows + 1;
o_employeeid (l_fetched_rows) := l_employeeid;
o_employeename (l_fetched_rows) := l_employeename;
l_result := DBMS_SQL.FETCH_ROWS (l_cursor);
END LOOP;
DBMS_SQL.CLOSE_CURSOR (l_cursor);
DBMS_OUTPUT.PUT_LINE (o_employeeid (1));
DBMS_OUTPUT.PUT_LINE (o_employeename (1));
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('GENERAL FAILURE: ' || SQLERRM);
END;
If we have a column in a table of type number, how can we store the result of select query on that column in an array ?
This sample uses a list (table of numbers) to achieve this, because i find
those lists much more handy:
CREATE OR REPLACE TYPE numberlist AS TABLE OF NUMBER;
DECLARE
v_numberlist numberlist;
BEGIN
SELECT intval numbercolumn
BULK COLLECT INTO v_numberlist
FROM lookup;
FOR i IN 1..v_numberlist.count
LOOP
dbms_output.put_line( v_numberlist(i) );
END LOOP;
END;
Create a type which store number:-
CREATE OR REPLACE TYPE varray is table of number;
--write your select query inside for loop () where i am extracting through level
declare
p varray := varray();
BEGIN
for i in (select level from dual connect by level <= 10) loop
p.extend;
p(p.count) := i.level;
end loop;
for xarr in (select column_value from table(cast(p as varray))) loop
dbms_output.put_line(xarr.column_value);
end loop;
END;
output:-
1
2
3
4
5
6
7
8
9
10
Just an option to use some native SQL datatype. Hope it helps.
SET SERVEROUTPUT ON;
DECLARE
lv_num_tab DBMS_SQL.NUMBER_TABLE;
BEGIN
SELECT LEVEL BULK COLLECT INTO lv_num_tab FROM DUAL CONNECT BY LEVEL < 10;
FOR I IN lv_num_tab.FIRST..lv_num_tab.LAST
LOOP
dbms_output.put_line(lv_num_tab(i));
END LOOP;
END;
You may also want to put the whole select in a table. You can use a BULK COLLECT to an array:
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
PROCEDURE get_tables(p_owner in varchar2)
as
v_res t_my_list;
v_qry varchar2(4000) := '';
begin
v_qry := ' SELECT table_name from all_tables where owner='''||p_owner||'''';
dbms_output.put_line(v_qry);
-- all at once in the table
execute immediate v_qry bulk collect into v_res;
FOR I in 1..v_res.count
loop
dbms_output.put_line(v_res(i));
end loop;
exception
when others then
raise;
end get_tables;
/
begin
get_tables('E') ;
end;
/
I am trying to implement this procedure into a package however the package
will not allow me to use the cursor for some reason. Can anyone help? Thank you.
Also when I try to put the procedure into my package a 'enter bind variable' box appears
minus anywhere to input a bind variable and this error
Not found
The requested URL /apex/wwv_flow.show was not found on this server
My code is
PROCEDURE total_calc(p_order NUMBER)
IS
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
v_c1 REFCURSOR;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
dbms_output.put_line(c_prod_desc || ': ' || c_price);
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
dbms_output.put_line('Total Cost:' || v_total_cost);
END;
Here is the code for the rest of the package
CREATE OR REPLACE PACKAGE orders_salary_manage AS
FUNCTION tax_func (p_sal IN NUMBER)
RETURN NUMBER;
PROCEDURE reduce_price(p_product_id NUMBER, p_sub_price NUMBER);
PROCEDURE increase_price(p_product_id NUMBER, p_add_price NUMBER);
PROCEDURE remove_order(p_order_id NUMBER);
PROCEDURE add_order(p_order_id NUMBER,
p_order_date VARCHAR2,
p_delivery_date VARCHAR2,
p_customer_id NUMBER,
p_employee_id NUMBER,
p_order_type_id NUMBER);
END orders_salary_manage;
CREATE OR REPLACE PACKAGE BODY orders_salary_manage AS
tot_orders NUMBER;
FUNCTION tax_func (p_sal IN NUMBER)
RETURN NUMBER
IS
tax_rate NUMBER := 0;
v_netsal NUMBER := 0;
v_sal NUMBER;
BEGIN
v_sal := p_sal;
IF v_sal > 70000 THEN
tax_rate := (v_sal * 0.4);
v_netsal := v_sal - tax_rate;
END IF;
IF v_sal < 70000 THEN
tax_rate := (v_sal * 0.2);
v_netsal := v_sal - tax_rate;
END IF;
RETURN v_netsal;
END;
PROCEDURE reduce_price(p_product_id NUMBER, p_sub_price NUMBER)
IS v_price NUMBER;
e_invalid_price EXCEPTION;
BEGIN
SELECT unit_price
INTO v_price
FROM product
WHERE product_id = p_product_id;
v_price := v_price - p_sub_price;
IF v_price < 1 THEN
RAISE e_invalid_price;
ELSE
UPDATE product SET unit_price = v_price WHERE product_id = p_product_id;
END IF;
END;
PROCEDURE increase_price(p_product_id NUMBER, p_add_price NUMBER)
IS v_price NUMBER;
BEGIN
SELECT unit_price
INTO v_price
FROM product
WHERE product_id = p_product_id;
v_price := v_price + p_add_price;
UPDATE product SET unit_price = v_price WHERE product_id = p_product_id;
END;
PROCEDURE remove_order(p_order_id NUMBER)
IS
BEGIN
DELETE FROM placed_order WHERE order_id = p_order_id;
DELETE FROM order_line WHERE fk1_order_id = p_order_id;
END;
PROCEDURE add_order(p_order_id NUMBER,
p_order_date VARCHAR2,
p_delivery_date VARCHAR2,
p_customer_id NUMBER,
p_employee_id NUMBER,
p_order_type_id NUMBER)
IS new_order NUMBER;
BEGIN
INSERT INTO placed_order (order_id, order_date, delivery_date, fk1_customer_id, fk2_employee_id, fk3_order_type_id)
VALUES (p_order_id, p_order_date, p_delivery_date, p_customer_id, p_employee_id, p_order_type_id);
END;
END;
This is really confusing.
First off - dbms_output requires special settings for the environment in order to work. And a call to DBMS_OUTPUT.ENABLE, plus packages do have access to a terminal anyway. Packages do not have your process context, they run in the process context of the db -- which runs without a controlling terminal. If you have to write something use a file -- UTL_FILE package is meant for that.
Next
Since you are basically adding up an order, you need a function to return the value.
A procedure is not going to work. As you coded anyway.
Next
v_C1 is not used and makes no sense for your code. You are not returning a refcursor.
Next
You should consider using a schema for tables with generic table names like that when you decalre a variable.
example:
v_foo schema_owner.table_name.column_name%type;
Consider this:
function total_calc(p_order in NUMBER)
return NUMBER
is
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
-- lose this: v_c1 REFCURSOR;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
-- lose this line: dbms_output.put_line(c_prod_desc || ': ' || c_price);
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
-- lose this line: dbms_output.put_line('Total Cost:' || v_total_cost);
return v_total_cost;
END;
In order for this to work it has to inside a
CREATE OR REPLACE FUNCTION a statement. Then - Added to the db schema by a user with correct permissions to create it.
After all that, then you can run it. Also, there is more to all this package/function creation stuff like using PRAGMAS if somebody needs to run this from SQLPLUS.