PL / SQL Function to return varchar2 / numbers - sql

I have this PL / SQL function that accepts the name of a student (f_name). The function then displays all of the information for the given student from a premade table called students. The table contains 5 columns, 2 number type, and 3 varchar2 type. If the name isn't found in the table an error message is returned. My code so far is
CREATE OR REPLACE FUNCTION studentName(
f_name IN VARCHAR2)
RETURN
IS
v_test students%rowtype;
CURSOR c1
IS
SELECT * FROM students WHERE first_name = f_name;
BEGIN
OPEN c1;
FETCH c1 INTO v_test;
IF c1%notfound THEN
v_test := NULL;
END IF;
CLOSE c1;
RETURN v_test;
END;
I keep getting:
PLS-00382: expression is of wrong type
I believe from my initial return varchar2 statement. How do I allow the return to accept both varchar2 type and number type?

RETURN varchar2
You need to return the rowtype, but you are returning a scalar. VARCHAR2 cannot hold a row, it can hold only a string value.
Modify it to:
RETURN students%rowtype;
Demo using standard EMP table:
SQL> CREATE OR REPLACE FUNCTION studentName(
2 f_name IN VARCHAR2)
3 RETURN emp%rowtype
4 IS
5 v_test emp%rowtype;
6 CURSOR c1
7 IS
8 SELECT * FROM emp WHERE ename = f_name;
9 BEGIN
10 OPEN c1;
11 FETCH c1 INTO v_test;
12 IF c1%notfound THEN
13 v_test := NULL;
14 END IF;
15 CLOSE c1;
16 RETURN v_test;
17 END;
18 /
Function created.
SQL> sho err
No errors.
NOTE : %ROWTYPE implies PL/SQL record type and PL/SQL types are not known to SQL. So you won't be able to use the function directly in plain SQL. You need to use SQL object type. Else you will get:
ORA-06553: PLS-801: internal error [55018]
Workaround to use it in SQL:
SQL> create or replace
2 type student_obj_type
3 as object(
4 student_id number,
5 stu_name varchar2(20),
6 dept varchar2(20)
7 )
8 /
Type created.
Use student_obj_type instead of students%rowtype to use the function in SQL.

Related

Store a list of certain numbers globally and use it as the value of parameter in a PL/SQL function

I have certain numbers eg., (1,2,3,7,8) which need to be stored in an object (let's say, l_list) and this l_list will used as the value of the parameter in a function while calling a select query.
Storing data globally:
l_list := (1,2,3,7,8);
Creating a function:
create or replace function f_get_result (p_list number)
return varchar2
as
l_result varchar2(100);
Begin
select l_code
into l_result
from table_1;
return l_result;
end f_get_result;
Calling the function in select query:
select f_get_result(l_list) from dual;
Till now I have tried to define the datatype of the parameter as type table and varray. But, it didn't work.
Here is the code which I used:
-- creating a type ---
CREATE OR REPLACE TYPE num_array AS TABLE OF varchar2(100);
-- the function ---
create or replace function fn_id_exp(p_branch in num_array)
return varchar2
is
txt varchar2(1000);
txt_1 varchar2(1000);
begin
for i in 1..p_branch.count loop
select branch_name into txt_1 from tbl_branch
where branch_code = p_branch(i);
txt := txt ||txt_1||chr(10);
end loop;
return txt;
end;
-- the select query ---
select fn_id_exp(num_array('100','200')) from dual;
expectation: select fn_id_exp(l_list) from dual; , need to use this much, num_array should not be there,
where l_list is an object which is storing certain numbers like (100,200,300).
Please, help me in this, I am bit new in this. Can somebody, give me an an appropriate example regards this?
Note: It doesn't matter where and what datatype these number are getting stored. Requirement is just to call all the numbers(1,2,3,7,8) in this:
select fn_id_exp(l_list) from dual;
Another option, which doesn't require you to use your own type, is to use Oracle's built-in types.
Here's an example; function returns employees that work in departments passed as a collection.
SQL> create or replace function f_test (par_deptno in sys.odcinumberlist)
2 return sys.odcivarchar2list
3 is
4 retval sys.odcivarchar2list;
5 begin
6 select ename
7 bulk collect into retval
8 from emp
9 where deptno in (select * from table(par_deptno));
10
11 return retval;
12 end;
13 /
Function created.
Testing:
SQL> select * from table(f_test(sys.odcinumberlist(10, 20)));
COLUMN_VALUE
--------------------------------------------------------------------------------
MILLER
KING
CLARK
FORD
ADAMS
SCOTT
JONES
SMITH
8 rows selected.
SQL>
Note that such a result might be more convenient than returning a string which "mimics" rows in a table (whose values are separated by line feed character), as you'd still might need to split them into rows if you wanted to further process the result.
You said:
expectation: select fn_id_exp(l_list) from dual; , need to use this much, num_array should not be there
But - why not? What's wrong with num_array (or whichever type name you'd use)? That's the syntax, you should follow it if you want to get the result. Silly requirements, in my opinion.
Anyway: you could pass a string of e.g. comma-separated numeric values such as in this example; you'll have to split them to rows. See if it helps.
SQL> create or replace function f_test (par_deptno in varchar2)
2 return sys.odcivarchar2list
3 is
4 retval sys.odcivarchar2list;
5 begin
6 select ename
7 bulk collect into retval
8 from emp
9 where deptno in (select regexp_substr(par_deptno, '[^,]+', 1, level)
10 from dual
11 connect by level <= regexp_count(par_deptno, ',') + 1
12 );
13
14 return retval;
15 end;
16 /
Function created.
Testing:
SQL> select * from table(f_test('10, 20'));
COLUMN_VALUE
--------------------------------------------------------------------------------
SMITH
JONES
CLARK
SCOTT
KING
ADAMS
FORD
MILLER
8 rows selected.
SQL>
Use a collection:
CREATE TYPE number_array AS TABLE OF NUMBER;
Then:
CREATE FUNCTION fn_id_exp(
p_branch in number_array
) return varchar2
IS
txt varchar2(1000);
BEGIN
SELECT LISTAGG(branch_name, CHR(10)) WITHIN GROUP (ORDER BY rn)
INTO txt
FROM (SELECT ROWNUM AS rn,
COLUMN_VALUE AS id
FROM TABLE(p_branch)) i
INNER JOIN tbl_branch b
ON (i.id = b.branch_code);
RETURN txt;
END;
/
Then:
select fn_id_exp(number_array(100,200)) from dual;
Given the sample data:
CREATE TABLE tbl_branch (branch_code, branch_name) AS
SELECT 100, 'Leafy Stem' FROM DUAL UNION ALL
SELECT 200, 'Big Twig' FROM DUAL;
Outputs:
FN_ID_EXP(NUMBER_ARRAY(100,200))
Leafy StemBig Twig
However
It may be simpler to not use a function and just use a query:
SELECT branch_name
FROM tbl_branch
WHERE branch_code IN (100, 200)
ORDER BY branch_code;
or, if you want to use the array::
SELECT branch_name
FROM tbl_branch
WHERE branch_code MEMBER OF number_array(100, 200)
ORDER BY branch_code;
or, pass in an array as a bind parameter (via JDBC, etc.) then:
SELECT branch_name
FROM tbl_branch
WHERE branch_code MEMBER OF :your_array
ORDER BY branch_code;
Which output:
BRANCH_NAME
Leafy Stem
Big Twig
fiddle
Update
If you want a static array then you can create a package:
CREATE PACKAGE static_data IS
FUNCTION getList RETURN NUMBER_ARRAY;
END;
/
With the body:
CREATE PACKAGE BODY static_data IS
list NUMBER_ARRAY;
FUNCTION getList RETURN NUMBER_ARRAY
IS
BEGIN
RETURN list;
END;
BEGIN
list := NUMBER_ARRAY(100, 200);
END;
/
then you can use it in your query:
select fn_id_exp(static_data.getlist()) from dual;
or
SELECT branch_name
FROM tbl_branch
WHERE branch_code MEMBER OF static_data.getlist()
ORDER BY branch_code;
fiddle

Find id then assign 1 if id found from table PL sql create procedure

I'm looking to create a procedure that looks for the given customer ID in the database. If the customer exists, it sets the variable found to 1. Otherwise, the found variable is set to 0. However, my call out code block does not provide a result. Did I miss something or my SELECT statement should be something else? Thank you.
CREATE OR REPLACE PROCEDURE find_customer(CUST_ID IN NUMBER, found OUT NUMBER) AS
CUSTID NUMBER := CUST_ID;
BEGIN
SELECT CUSTOMER_ID INTO CUSTID
FROM CUSTOMERS
WHERE CUSTOMER_ID = CUST_ID;
IF CUST_ID = NULL THEN
found := 1;
END IF;
EXCEPTION
WHEN no_data_found THEN
found := 0;
END;
/
DECLARE
CUSTOMER_ID NUMBER := 1;
found NUMBER;
BEGIN
find_customer(1,found);
DBMS_OUTPUT.PUT_LINE (found);
END;
I don't think there's anything other to it than the following part bellow. In your given example, it is not possible to get a null value from it as any null id would probably mean the item doesn't exist. Meaning it doesn't return a row, which triggers the NO_DATA_FOUND exception, which you catch.
This is what you wrote:
IF CUST_ID = NULL THEN
found := 1;
END IF;
This is probably what you meant:
IF CUST_ID IS NOT NULL THEN
found := 1;
END IF;
I'd rewrite it so that
you distinguish parameters from local variables from column names
use table aliases
fix what happens when something is found (is not null, line #11)
while testing, use variable you declared, not a constant (1)
So:
SQL> CREATE OR REPLACE PROCEDURE find_customer (par_cust_id IN NUMBER,
2 par_found OUT NUMBER)
3 AS
4 l_custid NUMBER;
5 BEGIN
6 SELECT c.customer_id
7 INTO l_custid
8 FROM customers c
9 WHERE c.customer_id = par_cust_id;
10
11 IF l_custid IS NOT NULL
12 THEN
13 par_found := 1;
14 END IF;
15 EXCEPTION
16 WHEN NO_DATA_FOUND
17 THEN
18 par_found := 0;
19 END;
20 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> SELECT * FROM customers;
CUSTOMER_ID
-----------
100
SQL> DECLARE
2 l_customer_id NUMBER := 1;
3 l_found NUMBER;
4 BEGIN
5 find_customer (l_customer_id, l_found);
6 DBMS_OUTPUT.put_line (l_found);
7 END;
8 /
0
PL/SQL procedure successfully completed.
SQL> DECLARE
2 l_customer_id NUMBER := 100;
3 l_found NUMBER;
4 BEGIN
5 find_customer (l_customer_id, l_found);
6 DBMS_OUTPUT.put_line (l_found);
7 END;
8 /
1
PL/SQL procedure successfully completed.
SQL>
You can simplify it down to:
CREATE OR REPLACE PROCEDURE find_customer(
p_cust_id IN CUSTOMERS.CUSTOMER_ID%TYPE,
p_found OUT NUMBER
) AS
BEGIN
SELECT 1
INTO p_found
FROM CUSTOMERS
WHERE CUSTOMER_ID = p_cust_id;
EXCEPTION
WHEN no_data_found THEN
p_found := 0;
END;
/
The line CUSTOMER_ID = p_cust_id will not match if either side is NULL so you don't need any further checks.
Then you can call it using:
DECLARE
v_found NUMBER;
BEGIN
find_customer(1,v_found);
DBMS_OUTPUT.PUT_LINE (v_found);
END;
/
db<>fiddle here

how to make a select query with functions pl sql

Function 1:
create or replace function get_books (l_id in number)
return varchar
is l_return varchar2(100);
begin
select books into l_return from people where id=l_id;
return l_return;
end
/
Function 2:
create or replace function get_author (l_id in number)
return varchar
is l_return varchar2(100);
begin
select author in l_return from authors where id=l_id;
return l_return;
end
/
I want to make a select with 2 functions, I want to display books and authors.
is it possible?
Yes, it is possible. For example:
select get_books (1) book,
get_author(1) author
from dual;
Presumably, both functions return a single value. If any of those selects (used in functions) returns more than one row, it'll fail.
As of the procedure: code you posted is invalid so I fixed it.
SQL> create or replace procedure get_categories (l_categories in varchar2,
2 l_id in number,
3 l_boolean out boolean,
4 l_error out varchar2)
5 is
6 begin
7 -- INSERT INTO categories (id, categories)
8 -- VALUES (l_categories, l_id);
9
10 l_boolean := true;
11 commit;
12 exception
13 when others
14 then
15 l_boolean := false;
16 l_error := sqlerrm;
17 end;
18 /
Procedure created.
As it returns 2 OUT parameters, you have to declare variables to accept those values (v_boolean and v_error). Furthermore, as you can't directly display Boolean value, use CASE and display a string instead.
Procedure can't be called as a function within the SQL SELECT statement so you have to use another PL/SQL piece of code; an anonymous PL/SQL block is OK.
SQL> set serveroutput on
SQL> declare
2 v_categories varchar2(10) := 'ABC';
3 v_id number := 1;
4 v_boolean boolean;
5 v_error varchar2(200);
6 begin
7 get_categories(l_categories => v_categories,
8 l_id => v_id,
9 l_boolean => v_boolean,
10 l_error => v_error
11 );
12
13 dbms_output.put_line(case when v_boolean then 'true' else 'false' end ||' - '||
14 v_error
15 );
16 end;
17 /
true -
PL/SQL procedure successfully completed.
SQL>

Count function rather than count(*)

I am practicing for final exams coming up and in my database class we were thrown a question asking to count the rows of a table by creating a function and returning that number.
I know how to create functions but I am stuck on how to go about this.
Can you put Count(*) inside of the function with a Select statement and return it?
In this example, function accepts a parameter (table name) and returns number of rows it contains. DBMS_ASSERT is used to prevent possible SQL injection.
SQL> CREATE OR REPLACE FUNCTION f_cnt (par_table_name IN VARCHAR2)
2 RETURN NUMBER
3 IS
4 retval NUMBER;
5 BEGIN
6 EXECUTE IMMEDIATE
7 'select count(*) from ' || DBMS_ASSERT.sql_object_name (par_table_name)
8 INTO retval;
9
10 RETURN retval;
11 END;
12 /
Function created.
SQL> SELECT f_cnt ('dual') FROM DUAL;
F_CNT('DUAL')
-------------
1
SQL>
Try this.
CREATE OR REPLACE FUNCTION TotalRecords
RETURN number IS
total number;
BEGIN
SELECT count(*) into total
FROM Your_table;
RETURN total;
END;

Array in IN() clause oracle PLSQL

I am passing String array(plcListchar) to Stored Procedure, i would like to use this String array in IN() clause.
i can not use plcListchar directly in IN() clause.
Let me show how i am crating plcListchar string array in JAVA.
String array[] = {"o", "l"};
ArrayDescriptor des = ArrayDescriptor.createDescriptor("CHAR_ARRAY", con);
ARRAY array_to_pass = new ARRAY(des,con,array);
callStmtProductSearch.setArray(4, array_to_pass);
for crating CHAR_ARRAY,
create or replace type CHAR_ARRAY as table of varchar2;
i want use plcListchar in IN clause. the following is my Stored Procedure.
CREATE OR REPLACE PROCEDURE product_search(
status IN varchar2,
plcList IN varchar2,
i_culture_id IN number,
plcListchar IN CHAR_ARRAY,
status_name OUT varchar2,
culture_code OUT varchar2)
AS
CURSOR search_cursor IS
SELECT p.status_name, p.culture_code
FROM PRISM_ITEM_cultures#prism p
WHERE p.publishable_flag=1
AND p.isroll =0
AND status = '-1'
AND p.plc_status IN (   );
BEGIN
OPEN search_cursor;
FETCH search_cursor INTO status_name, culture_code ;
CLOSE search_cursor;
END;
Could you please suggest me how to use, if you like to suggest any other logic, it is great.
Assuming that your collection is defined in SQL, not just in PL/SQL, you can use the TABLE operator (the definition you posted isn't syntactically valid-- you'd need to specify a length for the VARCHAR2)
AND p.plc_status IN (SELECT column_value
FROM TABLE( plcListchar ))
Since I don't have your tables, an example using the SCOTT schema
SQL> create type ename_tbl is table of varchar2(30);
2 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_enames ename_tbl := ename_tbl( 'KING', 'SMITH' );
3 begin
4 for i in (select *
5 from emp
6 where ename in (select column_value
7 from table( l_enames )))
8 loop
9 dbms_output.put_line( 'ENAME = ' || i.ename );
10 end loop;
11* end;
SQL> /
ENAME = KING
ENAME = SMITH
PL/SQL procedure successfully completed.