Table EMP has ENAME as attribute.The following function gives error:
SET SERVEROUTPUT ON
SET ECHO ON
CREATE OR REPLACE FUNCTION count_emp(e_name varchar(20))
RETURN integer IS
total integer;
BEGIN
SELECT count(*) into total
FROM DEPARTMENTS
where ENAME = e_name;
RETURN total;
END;
/
warning:function created with compilation error.
You can run show errors; to see what compilation errors are.
The parameter's datatype should be specified without length. Also, use varchar2 instead of varchar.
From Oracle site:
The VARCHAR datatype is synonymous with the VARCHAR2 datatype. To avoid possible changes in behavior, always use the VARCHAR2 datatype to store variable-length character strings.
Try this:
CREATE OR REPLACE FUNCTION count_emp(e_name varchar2) -- here
RETURN integer IS
total integer;
BEGIN
SELECT count(*) into total
FROM DEPARTMENTS
where ENAME = e_name;
RETURN total;
END;
/
If you care about table EMP, you should use it in the function.
I would write this as:
CREATE OR REPLACE FUNCTION count_emp (
in_e_name varchar2
)
RETURN integer IS
v_total integer;
BEGIN
SELECT COUNT(*) into v_total
FROM EMP e
WHERE e.ENAME = in_e_name;
RETURN v_total;
END;
Notes:
Oracle will compile functions and stored procedures even when the objects don't (yet) exist. This is considered a "feature".
Use naming conventions to distinguish parameters and variables from columns. This is using in_ for the input parameters and v_ for the local variables.
Qualify all column name references. This further reduces the possibility of collision between a variable and column name.
You don't need a length for varchar2() inputs (which is preferable to varchar(), although perhaps one day, Oracle will cave to the standard).
Related
This code works:
WITH
FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
b(v) AS (SELECT column_value FROM sys.ODCINUMBERLIST(1,2,3))
SELECT a (v) FROM b;
But I would like to define a type in this with statement. Later I want to reuse this type in order to use a pipelined function. Therefore I will need a type which is a table of a record. And the type must be defined outside of the function (not inside the function ) because the type will be returned by the function
I tried with this simple type w.
WITH
type w is record(w1 integer);
FUNCTION f (a in integer)
RETURN INTEGER
IS
ret integer;
BEGIN
return 2;
END;
B(b1) as (select 1 from dual)
select f(3) from dual;
It doesn't work:
[Error] Compilation (3: 5): ORA-00900: invalid SQL statement
Is it possible to define a type within a with statement and how can I do that?
The return data type of a function used in a SQL context (as opposed to PL/SQL) must be either a native SQL type or a schema-level user-defined type (a type defined on its own, with the create type statement, rather than defined in a package).
This restriction was not lifted when support for functions defined in the with clause was added in Oracle 12.1 - not even for the data type of a function so defined. In particular, the return data type can't be defined in the with clause of a SQL statement.
Then, records are supported only in PL/SQL; at the schema level, you will need to create an object type, rather than a record type.
NOTE: A pipelined function returning a collection of records (with the record type defined in a package) can be used in a SQL context; the reason is that Oracle defines a corresponding object type, at the schema level, behind the scenes, and takes care of the conversion. Why Oracle doesn't do the same for all functions is something only Oracle can explain. Of course, as you are finding out in another thread you started, pipelined table functions in the with clause are not supported (even though non-pipelined table functions are!)
We don't know the actual problem you are trying to solve, but it seems unlikely that you will be able to do everything you are trying to do in the with clause.
You cannot.
If you look at the SELECT documentation, particularly the syntax diagrams:
with_clause::=
plsql_declarations::=
You can see that you can only declare a function or a procedure.
If you try:
WITH FUNCTION f (a in integer)
RETURN w
IS
type w is record(w1 integer);
BEGIN
return w(2);
END;
SELECT f(3)
FROM DUAL;
You get the error:
ORA-06553: PLS-313: 'F' not declared in this scope
ORA-06552: PL/SQL: Item ignored
ORA-06553: PLS-498: illegal use of a type before its declaration
You cannot fix that error but, if you magically could (which you cannot using only local declarations), you would get a second error as a RECORD is a PL/SQL only data type and you are then trying to use it in an SQL scope.
For example, if you declared the type globally in a PL/SQL package:
CREATE PACKAGE pkg AS
type w is record(w1 integer);
END;
/
WITH FUNCTION f (a in integer)
RETURN pkg.w
IS
BEGIN
return pkg.w(2);
END;
SELECT f(3).w1
FROM DUAL;
The query gives the error:
ORA-00902: invalid datatype
You would need to use an SQL OBJECT type and declare it in the global SQL scope before running your query.
For example:
CREATE TYPE w IS OBJECT(w1 INTEGER);
WITH FUNCTION f (a in integer)
RETURN w
IS
BEGIN
return w(2);
END;
SELECT f(3).w1
FROM DUAL;
Outputs:
F(3).W1
2
To use a RECORD, you need to declare the type in a PL/SQL package or in a PL/SQL anonymous block and then use it only in PL/SQL.
For example, if you just want to run your function using a locally declared PL/SQL type then you can do it entirely in a PL/SQL anonymous block:
DECLARE
TYPE w IS record(w1 integer);
v_w w;
FUNCTION f (a in integer)
RETURN w
IS
BEGIN
return w(2);
END f;
BEGIN
v_w := f(3);
DBMS_OUTPUT.PUT_LINE(v_w.w1);
END;
/
Outputs:
2
db<>fiddle here
Your code will work if you define the type within the function like so:
WITH
FUNCTION f (a in integer)
RETURN INTEGER
IS
ret integer;
type w is record(w1 integer);
BEGIN
return 2;
END;
B(b1) as (select 1 from dual)
select f(3) from dual;
I have stored function which is returning Varchar2 (32767) and I want to use it in select statement under IN But it gives me error when i use it in Select under IN clause.
SELECT * FROM testcustomers1 where no_of_bu1 in(select myMaxLenFunc('test') from dual);
It gives me error
Error :-
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
If the return value is less than 4k it works fine but if it is greater than that it throws the above error.
Please suggest me if I use varchar2 table or Varchar2 are return in stored function how can i use it IN clause in select.
You have the right idea using a collection instead of a string in the IN clause. Then you will not run into this problem. Try something like this:
CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2 (4000)
/
CREATE OR REPLACE FUNCTION strings
RETURN strings_t
AUTHID DEFINER
IS
BEGIN
RETURN strings_t ('abc', 'def', '123');
END;
/
CREATE TABLE t (s VARCHAR2 (100))
/
BEGIN
INSERT INTO t
VALUES ('abd');
INSERT INTO t
VALUES ('def');
INSERT INTO t
VALUES ('456');
COMMIT;
END;
/
SELECT *
FROM t
WHERE t.s IN (SELECT COLUMN_VALUE FROM TABLE (strings ()))
/
Your function is PL/SQL and can return a varchar2 string of more than 4000. This is illegal for SQL (if the MAX_STRING_SIZE parameter is of value STANDARD)
http://docs.oracle.com/cd/E11882_01/appdev.112/e17126/datatypes.htm
VARCHAR2 Maximum Size in PL/SQL: 32,767 bytes Maximum Size in SQL
4,000 bytes
So you need to find a way around. Since no_of_bu1 is a SQL column and cannot have more than 4000 bytes in content length you are save with this:
SELECT * FROM testcustomers1
where no_of_bu1 in(select substr(myMaxLenFunc('test'),1,4000) from dual);
Although I would truncate the string within the function.
If your DB is of Oracle 12.1 you can find out if your are STANDARD in SQL stringsize (i.e. 4000)
SELECT name, value
FROM v$parameter
WHERE name = 'max_string_size'
I want to write a function which can be used to format a faculty member's salary (FACULTY being a table, where each row is a member) to $9,999,999.99 without hard coding the exact salary datatype (i.e the function should work even if in the future minor changes are made to the salary data size/type). I want to be able to use this function in a SQL statement for displaying a faculty member's salary.
Here is what my function code looks like right now.
CREATE OR REPLACE FUNCTION FUNC_FORMAT_SAL(fid IN FACULTY.F_ID%TYPE)
RETURN NUMBER IS
sal NUMBER;
BEGIN
SELECT FACULTY.F_SALARY INTO sal FROM FACULTY
WHERE FACULTY.F_ID = fid;
RETURN sal;
END;
I understand that in PLSQL a function cannot return a void right? That is why I have taken the member's F_ID as an IN input into the function. Inside this function, I want to format the SALARY for that member so that it could be displayed when I call an SQL statement, and then return the formatted salary.
Am I making any sense? This is a tutorial question in class and I just can seem to understand even though easy it appears to be.
I would say your function should return a VARCHAR2 if you are going to use it for display purpose. Taking a step further, it would be better to parameterise the format model as well. It should include exception handling such that when there is no data for the id it should return NULL and raise an exception for other errors( such as invalid format model).
CREATE OR REPLACE FUNCTION func_format_sal(p_fid IN faculty.f_id%TYPE,
p_format IN VARCHAR2 DEFAULT '$9,999,999.99')
RETURN VARCHAR2
IS
v_sal VARCHAR2(40);
BEGIN
SELECT TO_CHAR(f.f_salary, p_format)
INTO v_sal
FROM faculty f
WHERE f.f_id = p_fid;
RETURN v_sal;
EXCEPTION
WHEN no_data_found THEN
RETURN NULL;
WHEN OTHERS THEN
RAISE;
END;
/
You may call this function in 2 ways, assuming 1,2 are valid f_ids
select func_format_sal(1) FROM dual;
select func_format_sal(2,'$99999') FROM dual;
Here's a dbfiddle demo
I'm trying to create my first oracle procedure. The select will return multiple records; I need to be able to place each record in the variables and use the record in later actions in the procedure. Any help please?
key number;
keyCount number;
rub varchar2(50);
srub varchar2(100);
type varchar2(200);
date varchar2(14);
note varchar2(500);
BEGIN
SELECT KEY,COUNT(KEY),RUB,
SRUB,TYPE ,DATE,NOTE FROM Student
WHERE S_KEY = {key};
END;
In PL/SQL we need to select results into matching variables. One way is separate variables for each column (as shown). The alternative is to use a row variable which matches the project of the query; find out more.
You've got an aggregating function - COUNT() so you need a GROUP BY clause which defines the non-aggregating columns. You say you have more than one record so you need to populate a collection not scalar variables. Find out more.
Your procedure should look something like this
create or replace procedure my_first_proc
( p_key in student.s_key%type )
as
type my_rec is record (
key number ,
keyCount number ,
rub varchar2(50); ,
srub varchar2(100) ,
type varchar2(200) ,
date varchar2(14),
note varchar2(500)
);
type my_rec_coll is table of my_rec;
l_student_recs my_rec_coll;
BEGIN
SELECT KEY,COUNT(KEY),RUB,SRUB,TYPE ,DATE,NOTE
bulk collect into l_student_recs
FROM Student
WHERE S_KEY = p_key
group by KEY,RUB,SRUB,TYPE ,DATE,NOTE
;
for idx in l_student_recs.first() .. l_student_recs.last()
loop
-- do some processing here
dbms_output.put_line('RUB = '||l_student_recs(idx).rub);
end loop;
EXCEPTION
when no_data_found then
raise_application_error(-01403, 'no student records for key='||p_key);
END;
Get into good habits:
use sensible variable names
distinguish parameter names from local variables
handle predictable exceptions
Hey there I have a function, and part of the function is to make sure that the selected value is within the passed in table of varchar2s. To start I declare a varchar2 table type like so.
create or replace type Varchar2Table is table of varchar2(200)
Then I have the function which accepts the nested table parameter and has a select statement on them.
function SelectPeople(inputNames Varchar2Table) return People
begin
--stuff
select * from person_table where name in inputNames; --line of interest
--more stuff
end;
This doesn't seem to work though, I get the following error:
ORA-00932: inconsistent datatypes: expected NUMBER got
ENGSPL5.VARCHAR2TABLE
Any suggestions?
The TABLE operator allows nested tables to be used in SQL statements. The function was also missing an IS and an INTO.
create or replace type Varchar2Table is table of varchar2(200);
create table person_table(id number, name varchar2(100));
create or replace function SelectPeople(inputNames Varchar2Table) return number
is --Missing "IS".
type numberTable is table of number; --Need a collection to store results.
numbers numberTable;
begin
select id
bulk collect into numbers --Missing "INTO".
from person_table
where name in (select column_value from table(inputNames)); --Missing "TABLE".
--Alternatively a multiset condition can be used.
--where name member of inputNames;
--Dummy return value to make the function compile.
return 1;
end;
/