How to fix "Warning: Function created with compilation errors"? - oracle

There are two tables
Customer2(CUSTID,NAME,ADDRESS,AGE,SALARY)
Order2(ORDERID, ORDERNAME,PRICE,CUSTID)
the main issue is to find total no of order made by the customer from the location
this is the plsql function of I wrote:
CREATE OR REPLACE FUNCTION totalCustomer (loc IN VARCHAR2) RETURN NUMBER IS
total number(2) := 0;
add CHAR := loc;
BEGIN
SELECT COUNT(*) FROM order2 WHERE cusid IN (SELECT cusid FROM customer2 WHERE address='add');
RETURN total;
END;
/
and this is where I'm calling the function:
DECLARE
p VARCHAR2(100);
BEGIN
p:= &p;
dbms_output.put_line (totalCustomer (p));
END;
/
I'm getting this error Warning: Function created with compilation errors.
and this one while I'm calling the function
ERROR at line 5:
ORA-06550: line 5, column 23:
PLS-00905: object SYSTEM.TOTALCUSTOMER is invalid
ORA-06550: line 5, column 1:
PL/SQL: Statement ignored

In your function:
You wrote cusid in your query but the columns are named custid in the tables.
You don't put the query result in the variable total. Your function will always return 0 the initial value you set for total.
By writing 'add' you compared the address to the literal string "add" rather than the contents of the variable add.
But there's no need to copy loc to add. You can use loc directly.
The type of number(2) for total seems a little small. Better go with number(38) (integer) instead.
The query is better written with an inner join. IN with a subquery often does not perform well.
Instead of being char the type of loc should be the type of address in customer2. You can use customer2.address%TYPE here.
CREATE OR REPLACE FUNCTION totalcustomer (loc IN customer2.address%TYPE)
RETURN number
IS
total number(38) := 0;
BEGIN
SELECT count(*) INTO total
FROM order2 o
INNER JOIN customer2 c
ON c.custid = o.custid
WHERE address = loc;
RETURN total;
END;
/
And in your anonymous block:
You declared p as char which means char(1), i.e. p can only hold one character. I'm not sure this is what you want. You can go with customer2.address%TYPE here too.
DECLARE
p customer2.address%TYPE;
BEGIN
p := &p;
dbms_output.put_line(totalcustomer(p));
END;
/

Related

writing function but been getting this error

the question:
Write a blocK PL/SQL that display the total commission amount of a job id. Use function “compute_commission” that accepts a job id equal to 9 and return his total commission of all corresponding employees.
the error:
`Error at line 11: PLS-00103: Encountered the symbol "DECLARE"
CREATE OR REPLACE FUNCTION compute_commission (C_employee_id in number)
RETURN number
is `
the code:
CREATE OR REPLACE FUNCTION compute_commission (C_employee_id in number)
RETURN number
is
sum_commission number;
begin
select sum(job_id)
into sum_commission from employees
where employee_ref_id = C_employee_id;
return sum_commission;
end compute_commission;
declare
cal_sum_commission number;
begin
cal_sum_commission = compute_commission(cal_sum_commission);
dbms_output.put_line ('employee commission is: ' || compute_commission(cal_sum_commission);
end;
Should be something like this:
CREATE OR REPLACE FUNCTION compute_commission (C_employee_id IN NUMBER)
RETURN NUMBER
IS
sum_commission NUMBER;
BEGIN
SELECT SUM (job_id)
INTO sum_commission
FROM employees
WHERE employee_ref_id = C_employee_id;
RETURN sum_commission;
END compute_commission;
/
DECLARE
cal_sum_commission NUMBER := 12345;
BEGIN
cal_sum_commission := compute_commission (cal_sum_commission);
DBMS_OUTPUT.put_line (
'employee commission is: ' || cal_sum_commission);
END;
/
Note that I modified anonymous PL/SQL block and
added local variable's value (otherwise you'd pass NULL to the function) (you'll, of course, use some valid value; this - 12345 - is just an example)
used local variable in DBMS_OUTPUT.PUT_LINE
terminated statement with a semi-colon (you've had a colon)
fixed assignment operator (:= instead of just =)
Also, is sum_commision really sum of JOB_ID values? Looks strange to me ...

How to define a function and a query in the same with block?

I can create a subquery in a with block.
WITH b AS (SELECT 2 FROM DUAL)
SELECT *
FROM b;
I can create a function in a with block
WITH
FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
SELECT a (COLUMN_VALUE) FROM sys.ODCINumberList (1);
But I haven't succeeded to define a function and a query in the same with block.
WITH
b AS (SELECT 2 FROM DUAL);
FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
SELECT a(COLUMN_VALUE) FROM sys.ODCINum
[Error] Execution (13: 8): ORA-00904: "A": invalid identifier
(A is the name of function)
I have to use "," instead of ";" and other things likewise but to no avail
I know that I can define a function directly in a pl/sql package, but the function I want to create will be used only in one query. Thefore it's not worth doing that
In a WITH clause, the PL/SQL declarations need to come before the sub-query factoring clauses.
Therefore the function declaration needs to come first.
Also, a query can only return a single result set; therefore you need to combine the output using UNION or UNION ALL:
WITH FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
b (value) AS (
SELECT 1 FROM DUAL
)
SELECT value FROM b
UNION ALL
SELECT a(COLUMN_VALUE) FROM sys.ODCINUMBERLIST(2);
Which outputs:
VALUE
1
3
db<>fiddle here

How do I display a nested table in Oracle - PL/SQL?

I have an assignment where I'm supposed to create a user-defined function that will return all employees after 1968. My code is as follows:
First I create a new object, called emp_dobs, to hold the employees' firstname, lastname, and date of birth, using the same data types as the original employee table:
CREATE OR REPLACE TYPE emp_dobs AS OBJECT (
emp_fname VARCHAR2(20),
emp_lname VARCHAR2(20),
emp_dob DATE
);
/
Then I create emp_dobs_nested as a table of emp_dobs:
CREATE OR REPLACE TYPE emp_dobs_nested AS TABLE OF emp_dobs;
/
Lastly, I create a function that's supposed to return an emp_dobs_nested table:
CREATE OR REPLACE FUNCTION get_emp_dobs RETURN emp_dobs_nested
AS
dobs emp_dobs_nested;
BEGIN
SELECT emp_dobs(firstname, lastname, birthdate) BULK COLLECT INTO dobs
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
RETURN dobs;
END;
/
There is a weird quirk with compiling emp_dob_nested, however, where Oracle SQL Developer will display a new tab labeled "Output Variables - Log," and only show EMP_FNAME and EMP_LNAME. Despite that, everything compiles.
Now I want to test the function and display its results to prove that it works, but when I try this:
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
DBMS_OUTPUT.PUT_LINE(dobs);
END;
/
I get this error:
Error report -
ORA-06550: line 5, column 5:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
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:
What am I missing here? Thanks.
You cannot pass the whole collection to DBMS_OUTPUT, rather you must loop through it and display individual columns at each index.
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
FOR i IN 1..dobs.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
END LOOP;
END;
/
You can also use TABLE function to unnest a collection of objects into a relational resultset:
select * from table( get_emp_dobs )
Live demo: http://sqlfiddle.com/#!4/8cfb2/1
Another way of display is to use XML, try
DBMS_OUTPUT.PUT_LINE(XMLTYPE(dobs));
why not a create a view instead?
CREATE VIEW emp_view
AS
SELECT firstname, lastname, birthdate
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
You can also try this,
DECLARE
dobs emp_dobs_nested;
i NUMBER := 0;
BEGIN
dobs := get_emp_dobs;
LOOP
i := dobs.NEXT(i);
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
IF i = dobs.LAST THEN
EXIT;
END IF;
END LOOP;
END;
/

Error on calculating and returning a function

I need to calculate and return the total number of different task-types involved in a given project.
Input parameter is a project identifier (ie project_no or project_code), output is the number of different task_types.
This is the function:
CREATE OR REPLACE FUNCTION task_types (pno NUMBER)
RETURN NUMBER IS
return_var NUMBER;
BEGIN
SELECT COUNT (DISTINCT t.task_type_no)
INTO return_var
FROM stage s, task t, project p
WHERE p.project_no = pno
AND p.project_no = s.project_no
AND s.stage_id = t.stage_id;
RETURN return_var;
END;
/
SELECT project_no, task_types(project_no)
FROM project;
The error:
Error at line 17: PLS-00103: Encountered the symbol "/"
1. CREATE OR REPLACE FUNCTION task_types (pno NUMBER)
2. RETURN NUMBER IS
3. return_var NUMBER;
Does anyone know how to make this work?
Why use a function when SQL does it so much better?
SELECT p.project_no,
(SELECT COUNT(DISTINCT t.task_type_no)
FROM stage s
JOIN task t on (t.stage_id = s.stage_id)
WHERE s.project_no = p.project_no) as diff_task_type_count
FROM project p;

Return 2 values from a PL-SQL function

How can i return 2 values from a PL-SQL function?
I would not advocate creating a function with an OUT parameter for the second value, because I like to think of functions as a pure concept: a function performs an operation on one or more inputs to produce one output. It shouldn't change any of its arguments or have any other "side effects".
So if you need two outputs, write a procedure instead:
procedure get_sqrt_and_half
( p_input number
, p_sqrt OUT number
, p_half OUT number
)
is
begin
p_sqrt := sqrt(p_input);
p_half := p_input/2;
end;
A function can only return a single SQL type, but that can be a user-defined type with multiple values. I'd need to know more about the actual end requirements before I'd recommend this as a solution, but it is a possibility.
create or replace type a_b is object (a number, b number);
/
create or replace function ret_a_b return a_b is
begin
return a_b(1,2);
end;
/
select ret_a_b from dual;
select d.rab.a, d.rab.b from (select ret_a_b rab from dual) d;
You can return one value directly and another one as an OUT parameter. Or you return a record that contains both values. The first option is, in most cases, simpler to do.
**If you are wanting to use it in SQL, then you would need a pipelined function e.g.**
CREATE OR REPLACE TYPE myemp AS OBJECT
( empno number,
ename varchar2(10),
job varchar2(10),
mgr number,
hiredate date,
sal number,
comm number,
deptno number
);
CREATE OR REPLACE TYPE myrectable AS TABLE OF myemp ;
enter code here
CREATE OR REPLACE FUNCTION pipedata(p_min_row number, p_max_row number) RETURN myrectable PIPELINED IS
v_obj myemp := myemp(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
BEGIN
FOR e IN (select *
from (
select e.*
,rownum rn
from (select * from emp order by empno) e
)
where rn between p_min_row and p_max_row)
LOOP
v_obj.empno := e.empno;
v_obj.ename := e.ename;
v_obj.job := e.job;
v_obj.mgr := e.mgr;
v_obj.hiredate := e.hiredate;
v_obj.sal := e.sal;
v_obj.comm := e.comm;
v_obj.deptno := e.deptno;
PIPE ROW (v_obj);
END LOOP;
RETURN;
END;
SQL> select * from table(pipedata(1,5));
Try using OUT parameters:
create or replace function f(a IN NUMBER, b OUT NUMBER) RETURN NUMBER IS
BEGIN
b := a;
RETURN a;
END f;

Resources