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>
Related
I have been wrestling with this problem for a while now. I want to make a function that takes parameters. The function is supposed to, with user submitted parameters, change a select-statment and after return/run the select.
The function:
create or replace FUNCTION F_GET_TABLE(column1_in varchar2, column2_in varchar2)
return query
is
base_query constant varchar2(5000 char) :=
'select column1, column2 from CustomersTable;';
begin
replace(replace(base_query, 'column1', column2_in),'column2', column2_in );
queryToReturn query := base_query;
return queryToReturn;
end F_GET_TABLE;
In my head the end result should be that I call the function like this:
select F_GET_TABLE('f_name','e_mail') from dual;
And I should have the same result as if I wrote the select-statment as:
select f_name, e_mail from CustomersTable;
So I've tried in different ways to make the function return the query as I've described. However the best I managed to do was return a varchar2 with the select-statement - however then I have to remove "" from the start and end of the select-block and run it manually.. I couldn't seem to find any answers to my problem while searching the internet, please help me out here!
Here's how:
SQL> CREATE OR REPLACE FUNCTION f_get_table (column1_in VARCHAR2,
2 column2_in VARCHAR2)
3 RETURN VARCHAR2
4 IS
5 base_query VARCHAR2 (5000) := 'select column1, column2 from emp';
6 BEGIN
7 RETURN REPLACE (REPLACE (base_query, 'column1', column1_in),
8 'column2', column2_in);
9 END f_get_table;
10 /
Function created.
SQL> select f_get_table('ename', 'job') from dual;
F_GET_TABLE('ENAME','JOB')
--------------------------------------------------------------------------------
select ename, job from emp
SQL>
If you want to return result, then return ref cursor:
SQL> CREATE OR REPLACE FUNCTION f_get_table (column1_in VARCHAR2,
2 column2_in VARCHAR2)
3 RETURN SYS_REFCURSOR
4 IS
5 base_query VARCHAR2 (5000) := 'select column1, column2 from emp';
6 l_rc SYS_REFCURSOR;
7 BEGIN
8 OPEN l_rc FOR
9 REPLACE (REPLACE (base_query, 'column1', column1_in),
10 'column2',
11 column2_in);
12
13 RETURN l_rc;
14 END f_get_table;
15 /
Function created.
Testing:
SQL> SELECT f_get_table ('ename', 'job') FROM DUAL;
F_GET_TABLE('ENAME',
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ENAME JOB
---------- ---------
KING PRESIDENT
BLAKE MANAGER
CLARK MANAGER
JONES MANAGE
<snip>
I have my package header:
CREATE OR REPLACE PACKAGE my_package is
TYPE my_type IS
TABLE OF VARCHAR2(256) INDEX BY BINARY_INTEGER;
FUNCTION my_func return my_type;
END my_package;
And body with function my_func from which I return collection of restaurants:
CREATE OR REPLACE PACKAGE BODY my_package is
FUNCTION my_func RETURN my_type IS
restaurants_table my_type;
BEGIN
select ADRESS
BULK COLLECT INTO restaurants_table
from restaurants
FETCH NEXT 3 ROWS ONLY;
RETURN restaurants_table;
END my_func;
END my_package;
I want to call this function:
declare
TYPE my_type IS
TABLE OF VARCHAR2(256) INDEX BY BINARY_INTEGER;
restaurants_table my_type;
begin
restaurants_table := my_package.my_func();
end;
But I get error:
PLS-00382: expression is of wrong type
This happens when I try to assign return value from function to variable:
restaurants_table := my_package.my_func();
How can I call function correctly so I can have return value in my table type variable restaurants_table?
Later I want to print it by index:
dbms_output.put_line(restaurants_table(1));
I want to call this function:
should be
restaurants_table my_package.my_type;
instead of
TYPE my_type IS
TABLE OF VARCHAR2(256) INDEX BY BINARY_INTEGER;
restaurants_table my_type;
I don't have your table so I used Scott's DEPT:
SQL> CREATE OR REPLACE PACKAGE my_package is
2 TYPE my_type IS
3 TABLE OF VARCHAR2(256) INDEX BY BINARY_INTEGER;
4 FUNCTION my_func return my_type;
5 END my_package;
6 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY my_package is
2 FUNCTION my_func RETURN my_type IS
3 restaurants_table my_type;
4 BEGIN
5 select dname
6 BULK COLLECT INTO restaurants_table
7 from dept
8 FETCH NEXT 3 ROWS ONLY;
9
10 RETURN restaurants_table;
11 END my_func;
12 END my_package;
13 /
Package body created.
Testing:
SQL> declare
2 restaurants_table my_package.my_type;
3 begin
4 restaurants_table := my_package.my_func();
5 for i in 1 .. restaurants_table.count loop
6 dbms_output.put_line(restaurants_table(i));
7 end loop;
8 end;
9 /
ACCOUNTING
RESEARCH
SALES
PL/SQL procedure successfully completed.
SQL>
On the other hand, you could've used Oracle's built-in type for that purpose - sys.odcivarchar2list:
SQL> CREATE OR REPLACE PACKAGE my_package is
2 FUNCTION my_func return sys.odcivarchar2list;
3 END my_package;
4 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY my_package is
2 FUNCTION my_func RETURN sys.odcivarchar2list IS
3 restaurants_table sys.odcivarchar2list;
4 BEGIN
5 select dname
6 BULK COLLECT INTO restaurants_table
7 from dept
8 FETCH NEXT 3 ROWS ONLY;
9
10 RETURN restaurants_table;
11 END my_func;
12 END my_package;
13 /
Package body created.
SQL> set serveroutput on
SQL> declare
2 restaurants_table sys.odcivarchar2list;
3 begin
4 restaurants_table := my_package.my_func();
5
6 for i in 1 .. restaurants_table.count loop
7 dbms_output.put_line(restaurants_table(i));
8 end loop;
9 end;
10 /
ACCOUNTING
RESEARCH
SALES
PL/SQL procedure successfully completed.
SQL>
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
This stored procedure call returns a single varchar2 type value. The issue is that it comes back in a refcursor. I need to get the ID value and assign it to a variable instead of printing it to the console.
var r refcursor;
DECLARE
BEGIN
P_PACKAGE.INSERT_INVOICE(
IN_INVOICE_TYPE => L_INVOICE.INVOICE_TYPE,
OUTPUT => :R);
END;
/
print r;
Exactly as you said - you need to assign it to a variable. Here's an example based on Scott's schema.
SQL> create or replace procedure p_rc (par_deptno in number, par_rc out sys_refcursor)
2 is
3 begin
4 open par_rc for select ename, sal
5 from emp
6 where deptno = par_deptno;
7 end;
8 /
Procedure created.
Let's test it; pay attention to lines #4, 5 (declaration of variables) and #10 (fetch into those variables):
SQL> declare
2 l_rc sys_refcursor;
3 -- declare variables which will get values returned by refcursor
4 l_ename emp.ename%type;
5 l_sal emp.sal%type;
6 begin
7 p_rc(10, l_rc);
8
9 loop
10 fetch l_rc into l_ename, l_sal;
11 exit when l_rc%notfound;
12 dbms_output.put_line(rpad(l_ename, 10, ' ') ||': '|| l_sal);
13 end loop;
14 end;
15 /
CLARK : 2450
KING : 5001
MILLER : 1300
PL/SQL procedure successfully completed.
SQL>
I wouldn't define the REFCURSOR in SQL*Plus as you've shown it. Assuming that your REFCURSOR was opened with a statement similar to
SELECT ID
FROM SOME_TABLE
WHERE SOMEFIELD = some_value
then your PL/SQL code should look something like
DECLARE
aString VARCHAR2(2000);
rc SYS_REFCURSOR;
BEGIN
P_PACKAGE.INSERT_INVOICE(
IN_INVOICE_TYPE => L_INVOICE.INVOICE_TYPE,
OUTPUT => rc);
FETCH rc
INTO aString;
DBMS_OUTPUT.PUT_LINE('aString = ''' || aString || '''');
END;
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.