Oracle SQL user defined function - sql

I am trying to write Oracle SQL function. The should take country code, min year and max year as inputs and should return table which contains information for that country in the specified years. This is what I tried to write, but I am new to SQL functions. This is how the data looks and I will be glad for any help.
create or replace type african_crisis_row as object(
country_abv varchar(4),
year number(5),
banking_crisis varchar(10)
);
create or replace type t_african_crisis_table as table of african_crisis_row;
create or replace function african_crisis (
country_abv in varchar,
year_min in number,
year_max in number
)
return t_african_crisis_table as v_ret table t_african_crisis_table;
begin
select
african_crisis_row(country_abv, year)
bulk collect into
v_ret
from
africancrisisdata
where
country_abv = country_abv and year between year_min and year_max;
return v_ret
end african_crisis

You need to:
remove table after the v_ret declaration.
Include the 3rd banking_crisis value in the call to the african_crisis_row object constructor.
Include ; statement terminators after the return and final end statements.
Don't name the function parameters with the same name as the column values.
(Oracle uses VARCHAR2 and VARCHAR is an alias to VARCHAR2.)
Something like this:
create or replace function african_crisis (
i_country_abv in varchar2,
i_year_min in number,
i_year_max in number
) return t_african_crisis_table
as
v_ret t_african_crisis_table;
begin
select african_crisis_row(country_abv, year, banking_crisis)
bulk collect into v_ret
from africancrisisdata
where country_abv = i_country_abv
and year between i_year_min and i_year_max;
return v_ret;
end african_crisis;
/
db<>fiddle here

Related

How to create a function to add new record into a table? ORACLE

I am not sure if using the create or replace function is the right way to do this but i am trying to figure out how to ONLY add record to the table when the count of employee ID where month of read date = month of sysdate is not more than 10.
I have a employee_read table using primary key, reading_ID, foreign key emp_id and an attribute of read_date.
The information i found online shows return value. but how do i add into table instead of return? is it do-able?
Thank you for your help!
CREATE OR REPLACE FUNCTION AddNewReadRecord
(varEmpID IN NUMBER. varReadDate IN DATE, varWaterMeterID IN CHAR,
varCuReading IN NUMBER, varPrevReading IN Number)
RETURN
BEGIN
END
You can obviously add DML in the function but using PRAGMA AUTONOMOUS_TRANSACTION as follows:
CREATE OR REPLACE FUNCTION AddNewReadRecord
(varEmpID IN NUMBER. varReadDate IN DATE, varWaterMeterID IN CHAR, varCuReading IN NUMBER, varPrevReading IN Number)
RETURN number IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
Insert into your_table (...) -- column list
Select ... -- variable list
From your_table
Where emp_id = varEmpID
And read_date = varReadDate
Group by emp_id
Having count(1) < 10;
Commit;
Return 1; -- add logic for return using exception
END;
/

Oracle procedure, placement of multiple records in variables

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

PL/SQL returning contents of a table

Currently working on a .Net application for viewing the contents of a table, I faced difficulties with the function that returns the contents of the table.
I use the Oracle WebLogic Server to interface comics and application, I have a function that returns a desired record of the table but I unable to adapt to it returns all records of table.
I used tables, objects I have not been able to operate the pipeline.
Here is the function that returns a record.
CREATE OR REPLACE TYPE DMD_REC AS OBJECT
(
matricule VARCHAR2(10),
nom VARCHAR2(15),
prenom VARCHAR2(15),
adresse VARCHAR2(10),
profile VARCHAR2(15),
service VARCHAR2(15),
date_dmd DATE
);
CREATE OR REPLACE FUNCTION aff_dmd
(dmd_mat IN VARCHAR2)
RETURN DMD_REC IS
dmd_found demande%rowtype;
dmd_rtn DMD_REC;
BEGIN
SELECT *
INTO dmd_found
FROM demande
WHERE demande.matricule=dmd_mat;
dmd_rtn := DMD_REC
(
dmd_found.matricule,
dmd_found.nom,
dmd_found.prenom,
dmd_found.adresse,
dmd_found.profile,
dmd_found.service,
dmd_found.date_dmd
);
RETURN dmd_rtn;
END aff_dmd;
Sorry for my english
Your function returns an object which fits one row. You need it to return a table of such objects.
So first you need a table type:
CREATE OR REPLACE TYPE DMD_NT AS TABLE OF DMD_REC;
Then you have to convert your function to use this new type. In the absence of any other requirements this revision will select all records in the demande table when a NULL is passed:
CREATE OR REPLACE FUNCTION aff_dmd
(dmd_mat IN VARCHAR2 := null)
RETURN DMD_NT
IS
dmd_rtn DMD_NT;
BEGIN
SELECT DMD_REC
(
d.matricule,
d.nom,
d.prenom,
d.adresse,
d.profile,
d.service,
d.date_dmd
)
BULK COLLECT INTO dmd_rtn
FROM demande
WHERE
( dmd_mat is null
or d.matricule = dmd_mat
);
RETURN dmd_rtn;
END aff_dmd;
/
The BULK COLLECT populates a collection which is stored in session memory. If your table is large (say more than 5000) this may create problems with memory resources. In which case you should consider creating a pipelined function:
CREATE OR REPLACE FUNCTION aff_dmd
(dmd_mat IN VARCHAR2 := null)
RETURN DMD_NT PIPELINED
IS
dmd_rtn DMD_NT;
BEGIN
loop
SELECT DMD_REC
(
d.matricule,
d.nom,
d.prenom,
d.adresse,
d.profile,
d.service,
d.date_dmd
)
BULK COLLECT INTO dmd_rtn limit 1000
FROM demande
WHERE
( dmd_mat is null
or d.matricule = dmd_mat
);
exit when dmd_rtn.count() = 0;
for idx in 1..dmd_rtn.count() loop
pipe row (dmd_rtn(idx));
end loop;
end loop
RETURN;
END aff_dmd;
Pipelined functions have an overhead. You should consider whether there is a way you can make it work with SYS_REFCURSOR as #BobJarvis suggested.

How to use in statement with nested table

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;
/

Return multiple rows from a stored procedure

How do I create a stored procedure which can return multiple rows using SQL Developer BTW.?
Right now stored procedure returns the value for 1 row in 4 diff variables (there are 4 cols)
How I would go about making it so that it could return more than 1 row, for example if i were to query in my date it could return all the relevant data for that date instead of only 1.
create or replace PROCEDURE P2
(
ts IN TIMESTAMP,
u_id OUT VARCHAR2,
u_email OUT VARCHAR2,
cmnt OUT VARCHAR2
)
AS
BEGIN
SELECT U_ID , U_EML, C_TX INTO u_id, u_email, cmnt
FROM U_CM
WHERE U_CM_TS = ts;
END;
ts is the input timestamp
if i put in more a timestamp that has multiple rows associated with it i get an error?
How do i change the design so I can be successful in doing what i want? I am new to this so I dont know where to start
Use ref cursor:
create or replace PROCEDURE P2
(
ts IN TIMESTAMP,
p_result OUT sys_refcursor
)
AS
BEGIN
open p_result for
SELECT U_ID , U_EML, C_TX
FROM U_CM
WHERE U_CM_TS = ts;
END;
Also, try to give a more detailed names to columns and tables for more maintainable code. For example, user_id, user_email instead of u_id, u_eml. What is c_tx? I have no idea. Read about table and column naming conventions.