I want to return the values of several columns (With a function) in a varchar2 but I get an error when I choose several columns in select.
FUNCTION FU_PRUEBAS (P_IDNUM MAC.IDNUM%TYPE) RETURN VARCHAR2 IS REGISTRO VARCHAR2(100);
BEGIN
select NOMBRES, FECHANACIMIENTO INTO REGISTRO
from MAC WHERE IDNUM = P_IDNUM;
RETURN REGISTRO;
Error:
Error(17,6): PL/SQL: SQL Statement ignored
Error(18,6): PL/SQL: ORA-00947: there are not enough values
Two relatively simple options - if they suit your needs. You didn't really explain what you expect to get as a result so - this might, or might not be applicable to your situation.
Anyway, here you go:
First option uses listagg function which returns a concatenated string (as you wanted to return varchar2). Drawback is that it won't work for the result longer than 4000 characters, but - in that case - you can use XMLAGG.
SQL> create or replace function fu_pruebas (par_deptno in number)
2 return varchar2
3 is
4 retval varchar2(200);
5 begin
6 select listagg(ename, ', ') within group (order by ename)
7 into retval
8 from emp
9 where deptno = par_deptno;
10 return retval;
11 end;
12 /
Function created.
SQL> select fu_pruebas(10) result from dual;
RESULT
------------------------------------------------------------------------------
CLARK, KING, MILLER
SQL>
Another one concatenates different columns into a string; you might need to use e.g. to_char or to_date functions in certain cases, but - generally - it works as follows:
SQL> create or replace function fu_pruebas_2 (par_empno in number)
2 return varchar2
3 is
4 retval varchar2(200);
5 begin
6 select ename ||' - '|| job ||' - '|| sal
7 into retval
8 from emp
9 where empno = par_empno;
10 return retval;
11 end;
12 /
Function created.
SQL> select fu_pruebas_2(7654) result from dual;
RESULT
------------------------------------------------------------------------------
MARTIN - SALESMAN - 1250
SQL>
if you need a multiple values retunr you need a proper object
create or replace
type your_obj as object
( val_1 varchar2(200),
val_2 varchar2(200)
);
then
FUNCTION FU_PRUEBAS (P_IDNUM MAC.IDNUM%TYPE) RETURN VARCHAR2 IS REGISTRO VARCHAR2(100);
BEGIN
select NOMBRES, FECHANACIMIENTO INTO your_var1, your_var2
from MAC WHERE IDNUM = P_IDNUM
RETURN your_obj(your_var1, your_var2)
END;
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 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
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>
I am trying to concatenate strings that are used as column names
I want to do something like:
Select someData as "ONE" || :someVariable) from sometable;
where someVariable is a bind variable, which does not work inside double quotes.
(The column should have the name "ONE2018" if someVariable = 2018.)
I tried it with single quotes and with the concat function. It doesn't work.
Is there some way to accomplish this?
EDIT:
With inspiration from littlefoots answer I tried
declare
customVariable number(4);
rc sys_refcursor;
begin
open rc for 'select 1 as bla' || :customVariable || ' from dual';
dbms_sql.return_result(rc);
end;
/
which does have the output
BLA2018
----------
1
I don't know how to put that into a PreparedStatement, but if used on it's own it works and might help someone else
An example based on Scott's EMP table which contains columns whose names begins with an E: ENAME and EMPNO.
You'd pass NAME or MPNO and get the result.
SQL> create or replace function f_one (par_column_name in varchar2)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 l_str varchar2(200);
6 begin
7 l_str := 'select e' || par_column_name || ' from emp where rownum < 3';
8 open l_rc for l_str;
9 return l_rc;
10 end;
11 /
Function created.
SQL> select f_one('mpno') from dual;
F_ONE('MPNO')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO
----------
7369
7499
SQL> select f_one('name') from dual;
F_ONE('NAME')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ENAME
----------
SMITH
ALLEN
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.