expression is of wrong type for function - plsql

CREATE OR REPLACE FUNCTION k_w_b_salary(k IN NUMBER, b IN BOOLEAN)
RETURN EMP.ENAME%TYPE IS
names name_table;
BEGIN
IF (b = true) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL ASC)
WHERE ROWNUM <= k;
RETURN names;
ELSIF (b = false) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL DESC)
WHERE ROWNUM <= k;
RETURN names;
END IF;
END;
And I get this error:
12/9 PL/SQL: Statement ignored
12/16 PLS-00382: expression is of wrong type
20/9 PL/SQL: Statement ignored
20/16 PLS-00382: expression is of wrong type
I have this function that tries to find the best/worst paid employees. But i get the above error.
I think it's something to do with the ROWNUM but I am not sure.
I think the lines the error points out are not the lines with the error.
I had this function writen differently and the lines in the error where pointing to the ROWNUM <= k lines.
I have tried putting a fixed number there (<= 3) for example and I got the same error.
I have no idea what else to try, i can't really understand why this is not working.
It's not obvious to me why this is not working. I think it should work fine but obviously it dousen't.
The code for the table i use is :
CREATE OR REPLACE TYPE name_table IS TABLE OF VARCHAR2(10);
Any help is appreciated!

In the function declaration, you said
RETURN EMP.ENAME%TYPE
I assume the data type of column ENAME in table EMP is some sort of string (VARCHAR2(40) or similar) - right?
In the declarations section, you declare a variable names of data type name_table. You didn't show us the definition of the name_table type (that must be given outside the function, not in it); we can probably assume it is a nested table of some sort. Right? [EDIT - I take that back; you did show us your definition of name_table, at the end of your question.]
In the end, your function returns names. Which is of type name_table. But you said the function returns something else: EMP.ENAME%TYPE. In particular, you said the function returns a scalar data type, but you are returning a collection.
This will not work even if the collection has a single element. A table with a single "record" is not the same data type as the "record" itself - even if an actual table has a single "record" in it.
(And, much more so, when the table has three records in it!)
Rather: It seems that you want a table function: one that returns a table of things. If so, then declare the function that way. Perhaps you want the function to
RETURN NAME_TABLE
(at the top, in the function declaration)

Related

I m trying to make pipelined table function call but i m getting error in package body. Can u please make it correct

create or replace Type e_record AS Object(
e_uid number,
e_first_name varchar2(50) ,
e_last_name varchar2(50),
e_age number,
e_department varchar2(50),
e_designation varchar2(50),
e_salary number
);
create or replace Type e_record_table IS table Of e_record;
Create or replace package E_package
AS
function list_empDetails return e_record_table pipelined;
end E_package;
/
-------Package Body----------
Create or replace package body E_package
AS
Function list_empDetails return e_record_table pipelined
IS
e_ret e_record_table := e_record_table(null,null,null,null,null,null,null);
Begin
for x in(select e_uid,e_first_name,e_last_name,e_age,e_department,e_designation,e_salary into e_ret from Employee_details) Loop
pipe row(e_ret);
End Loop;
return;
end list_empDetails;
end E_package;
/
And the error i m getting is :
Error at line 8: PL/SQL: ORA-00947: not enough values
Error at line 9: PL/SQL: Statement ignored
Error at line 9: PLS-00382: expression is of wrong type
Error at line 8: PL/SQL: SQL Statement ignored
There are a couple of problems with your code.
Firstly, the not enough values error was because you are querying 7 columns and only providing one variable to put them in in the INTO clause. However, you don't use the INTO clause when looping over the results of a query that way, so the first thing you can do is to get rid of it. Your loop variable x will contain each row of data read from your Employee_details table, you don't need another variable for it.
The next thing to note is that you declared e_ret of type e_record_table. Your pipelined function returns e_record_table, but the type of each row you pipe out needs to be the type your table type contains, not the table type. The expression is of wrong type error is because you were attempting to pipe out a value of type e_record_table. Anyway, you're not selecting anything into e_ret any more, so you can just delete this local variable.
Your loop variable x contains each row read from the query. As written, it contains 7 values, so you could create an e_record row to pipe back using something like the following:
pipe row(e_record(x.e_uid, x.e_first_name, x.e_surname, x.e_age, ...));
However, that makes quite a lot of typing since you are repeating the list of columns in your query. You can avoid the duplicated typing by creating the record in the query and then piping that out:
for x in(select e_record(e_uid,e_first_name,e_last_name,e_age,e_department,e_designation,e_salary) as emp from Employee_details) Loop
pipe row(x.emp);
End Loop;
The full working function is below:
Function list_empDetails return e_record_table pipelined
IS
Begin
for x in(select e_record(e_uid,e_first_name,e_last_name,e_age,e_department,e_designation,e_salary) as emp from Employee_details) Loop
pipe row(x.emp);
End Loop;
return;
end list_empDetails;

PL/SQL Query for column and use it in the function call

I am trying this but sure I am missing a lot
declare
my_id table.ISR_ID%type;
begin
select NVL(MAX(table.ISR_ID)+1,1) into isr_id
from table;
select my_pkg.getFunction(InputToFunction=> isr_id); -- from ?
end;
If you declared MY_ID variable, you should have selected into it, not into ISR_ID (which is never declared).
Also, you should return function's result into something (probably another variable?). I've declared it as FUN_RES - see the comment within the PL/SQL anonymous block.
Saying that you are missing a lot doesn't help much; you should specify which errors you get. Anyway: try such a code, say whether it works or not and - if not - say why not (possible errors, etc.).
declare
my_id table.ISR_ID%type;
fun_res number; --> function result should be returned into this variable.
-- I don't know what it returns, so I set it to be a NUMBER.
-- Change it, if necessary.
begin
select NVL(MAX(table.ISR_ID) + 1, 1)
into my_id
from table;
fun_res := my_pkg.getFunction(my_id);
end;
[EDIT]
If you have to select function's value for every ISR_ID in a table, then you don't need PL/SQL but
select isr_id,
my_pkg.getfunction(isr_id) fun_res
from table;
If you want PL/SQL, then do it in a loop, for example:
begin
for cur_r in (select isr_id from table) loop
dbms_output.put_line(cur_r.isr_id ||', result = ' || my_pkg.getfunction(cur_r.isr_id));
end loop;
end;
/

Encountered the symbol "INTO" when expecting one of the following

I am encountering the following error when defining a function.
"Encountered the symbol "INTO" when expecting one of the following:..."
I am trying this example from a website. I also wanted to understand the cursors logic used here.
Can we fetch values from and close the cursor in definition part?
What is the significance of c1%notfound block and assigning a high value 9999 to p_cid?
create or replace function get_cust_id(p_cname IN varchar2)
RETURN number
IS
p_cid number;
cursor c1 is
select p_cid from customers where c_name = p_cname;
BEGIN
OPEN c1;
FETCH c1 INTO into p_cid;
if c1%notfound
then
p_cid:=9999;
end if;
close c1;
RETURN p_cid;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
============================================
The above "INTO" problem was rectified because of typo error.
I am now trying to call the function, but the output is not as expected :
select c_name,get_cust_id(c_name) as cust_id
from customers
where city='London';
Output :
C_NAME CUST_ID
Alice -
Patrick -
Aside from the double into that #Lukasz pointed out in comments, your cursor query is doing this:
select p_cid from customers where c_name = p_cname;
The p_cid here is the number you just declared, which hasn't been assigned a value yet. So you're effectively doing:
select null from customers where c_name = p_cname;
which is why your test query gets nulls back from the function calls. You need to change p_cid in that query to the column name in the table, which I'd guess is either just id or c_id:
create or replace function get_cust_id(p_cname IN varchar2)
RETURN number
IS
p_cid number;
cursor c1 is
select c_id from customers where c_name = p_cname;
BEGIN
OPEN c1;
FETCH c1 INTO p_cid;
...
Can we fetch values from and close the cursor in definition part?
No.
What is the significance of c1%notfound block and assigning a high value 9999 to p_cid?
If you pass in a name that does not exist (which can't happen the way you are calling it, but it's very contrived anyway) then the cursor query will find no data for that name. The fetch will not throw an error, but will not actually retrieve anything. The %notfound` cursor attribute is true, because no row was fetched.
In that situation, the if c1%notfound is true so the p_cid:=9999; statement is executed, and that value is later returned to the caller.
Effectively it's returning 9999 as a 'magic number' indicating that there was no actual ID found. (It would probably be safer to either return null, or throw an exception, if that happened - magic numbers can cause problems later, e.g. - fairly obviously - if you also have a real customer with ID 9999...)
There are simpler ways to achieve what this function is doing, but presumably it's trying to teach you specific things, in its contrived way.
The output was as expected when I changed the following line :
select c_id into p_cid from customers where c_name = p_cname;
Output :
C_NAME CUST_ID
Alice 3
Patrick 6

PL/SQL - LAST_VALUE return more than one row?

I am doing an school assigment where I need to get the last value of "code" so I can then insert next row with this code incremented. I tried to pull it out this way.
DECLARE
v_last_code f_shifts.code%TYPE;
BEGIN
SELECT LAST_VALUE(code) OVER (ORDER BY code)
INTO v_last_code
FROM f_shifts;
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
However I get ORA-01422: exact fetch returns more than one requested number of rows
and I have no idea why and how can a last_value be more than one row
Thanks !
You can use a nested table like this.
DECLARE
v_last_code f_shifts.code%TYPE;
TYPE t_tbl IS TABLE OF f_shifts.code%TYPE;
-- Above line creates the nested table type of the required type.
v_tbl T_TBL;
-- Above line creates the nested table variable.
BEGIN
SELECT code
BULK COLLECT INTO v_tbl -- collects all the values, ordered, into the nested table
FROM f_shifts
ORDER BY code;
v_last_code = v_tbl(v_tbl.LAST); -- gets the last values and puts into the variable
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
/

how to return a cursor or a result set from a oracle stored function

I have a stored function
CREATE OR REPLACE FUNCTION schedule(name in varchar2,pass in varchar2 )
begin
select t.name,s.starttime from traininfo t,schedule s, trainslot ts
where t.trainid in( select ts.trainid from trainslot
where ts.slotid in (select s.slotid from schedule s
where s.source='dhaka'
and s.dest='bogra' ))
end
I want to return this result set using a cursor.
I don't see where you are using either of the input parameters in your function. I'll assume that is either intentional or an oversight because you're simplifying the code. It also appears that your query is missing conditions to join between the traininfo, schedule, and trainslot tables in the outer query. It seems odd that your nested IN statements are turning around and querying the schedule and trainslot tables given this lack of join conditions. I don't know whether this is a result of copy-and-paste errors or something that was missed in posting the question or whether these are real problems. I'll make a guess at the query you're intending to write but if my guess is wrong, you'll have to tell us what your query is supposed to do (posting sample data and expected outputs would be exceptionally helpful for this).
CREATE OR REPLACE FUNCTION schedule(name in varchar2,pass in varchar2 )
RETURN sys_refcursor
is
l_rc sys_refcursor;
begin
open l_rc
for select t.name, s.starttime
from traininfo t,
schedule s,
trainslot ts
where t.trainid = ts.trainid
and s.slotid = ts.slotid
and s.source = 'dhaka'
and s.dest = 'borga';
return l_rc;
end;

Resources