Writing a procedure with an array as out parameter - oracle

I can't create a PL/SQL stored procedure. For example returns all of the ename of employees in deptno 20.
create table emp(
empno number(4,0),
ename varchar2(10),
job varchar2(9),
mgr number(4,0),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno number(2,0),
constraint pk_emp primary key (empno),
constraint fk_deptno foreign key (deptno) references dept (deptno)
);
This is what i manage to do but i can't seem to move/write it into a procedure.
declare
TYPE name_array IS TABLE OF emp.ename %type
INDEX BY BINARY_INTEGER;
enames name_array;
begin
update emp set empno = empno where deptno = 20
returning ename bulk collect into enames;
for i in 1..enames.count loop
dbms_output.put_line('ename : '||enames(i));
end loop;
end;
/

When returning a single object (which includes a table of values), I prefer functions over out parameters. Here is code that implements both.
drop table emp purge;
drop table dept purge;
CREATE TABLE dept
(
deptno INTEGER PRIMARY KEY
);
CREATE TABLE emp
(
empno NUMBER (4, 0)
, ename VARCHAR2 (10)
, job VARCHAR2 (9)
, mgr NUMBER (4, 0)
, hiredate DATE
, sal NUMBER (7, 2)
, comm NUMBER (7, 2)
, deptno NUMBER (2, 0)
, CONSTRAINT pk_emp PRIMARY KEY (empno)
, CONSTRAINT fk_deptno FOREIGN KEY (deptno) REFERENCES dept (deptno)
);
CREATE or replace PACKAGE emp_pkg
AS
TYPE name_array_t IS TABLE OF emp.ename%TYPE
INDEX BY BINARY_INTEGER;
FUNCTION upd (p_empno IN emp.empno%TYPE)
RETURN name_array_t;
PROCEDURE upd (
p_empno IN emp.empno%TYPE
, p_names OUT name_array_t
);
END emp_pkg;
CREATE or replace PACKAGE BODY emp_pkg
AS
FUNCTION upd (p_empno IN emp.empno%TYPE)
RETURN name_array_t
AS
l_names name_array_t;
BEGIN
UPDATE emp
SET empno = empno
WHERE deptno = 20
RETURNING ename
BULK COLLECT INTO l_names;
RETURN l_names;
END upd;
PROCEDURE upd (
p_empno IN emp.empno%TYPE
, p_names OUT name_array_t
)
AS
BEGIN
p_names := upd (p_empno);
END upd;
END emp_pkg;

This is a sample Stored Proc with return type as Array
create
procedure sample_proc(p_cust_id in number, p_customers out custarray)
as
my_cust custarray := custarray();

Related

oracle package and trigger

Create a database that contains four tables department, employee,
address and contact info, then Store 5 Records in each table.
Write the PL/SQL Statement for the following:
Store a record into employee, address and contact using trigger.
Retrieve departments and their employees using package
..........................
this is my code , what am i doing wrong ?
-- Question 2 :
-- Create Tables :
CREATE TABLE address (
code int primary key,
city varchar2(30),
street varchar2(30)
);
create table Department (
DepId int primary key ,
Dep_Name varchar2(30) ,
Dep_adress varchar(30));
create table Employee (
Emp_Id int primary key ,
firstName varchar2(30),
lastName varchar2(30),
salary int,
Dep_Id int references Department (DepId),
AdCode int references address (code));
CREATE TABLE contact_info (
email varchar2(30) primary key ,
phone int ,
EmpId int references Employee (Emp_Id));
-- insert :
insert into Department values (1,'IT','Amman');
insert into Department values (2,'CS','Jerash');
insert into Department values (3,'accounting','Amman');
insert into Department values (4,'managment','Amman');
insert into Department values (5,'employment','Amman');
insert into address values (50,'Amman','AAA');
insert into address values (60,'Amman','AAB');
insert into address values (70,'Amman','AAC');
insert into address values (80,'Jerash','AAD');
insert into address values (90,'Irbid','AAE');
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (1,'john' , 'samo' , 1000 , (select DepId from Department where Dep_Name = 'IT'),(select code from address where street = 'AAA'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (2,'mark' , 'wol' , 2000 , (select DepId from Department where Dep_Name = 'IT'),(select code from address where street = 'AAB'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (3,'ahmad' , 'moh' , 1100 , (select DepId from Department where Dep_Name = 'IT'),(select code from address where street = 'AAC'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (4,'maher' , 'imk' , 1700 , (select DepId from Department where Dep_Name = 'CS'),(select code from address where street = 'AAD'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (5,'ali' , 'geh' , 1200 , (select DepId from Department where Dep_Name = 'CS'),(select code from address where street = 'AAE'));
insert into contact_info values ('john#gmail.com',0785602200, (select Emp_Id from Employee where salary = 1000));
insert into contact_info values ('mark#gmail.com',0785602201, (select Emp_Id from Employee where salary = 2000));
insert into contact_info values ('ahmad#gmail.com',0785602202, (select Emp_Id from Employee where salary = 1100));
insert into contact_info values ('maher#gmail.com',0785602203, (select Emp_Id from Employee where salary = 1700));
insert into contact_info values ('ali#gmail.com',0785602204, (select Emp_Id from Employee where salary = 1200));
-- trigger :
CREATE OR REPLACE TRIGGER add_rec
AFTER INSERT
ON Department
FOR EACH ROW
DECLARE
Empl_id NUMBER;
BEGIN
INSERT INTO address(code,city,street)
VALUES(:NEW.code, :NEW.city, :NEW.street)
INSERT INTO contact_info(email, phone, EmpId)
VALUES(:NEW.email, :NEW.phone, :NEW.EmpId)
INSERT INTO Employee(Emp_Id,firstName,lastName,salary,Dep_Id,AdCode)
VALUES(:NEW.Emp_Id, :NEW.firstName, :NEW.lastName, :NEW.salary, :NEW.lastName, :NEW.Dep_Id, :NEW.AdCode)
END;
-- Package :
set serveroutput on;
CREATE OR REPLACE PACKAGE emp_dept AS
TYPE EmpDept IS RECORD (employee_id number , last_name varchar2(25) , department_id number);
CURSOR dept_employees RETURN EmpDept;
PROCEDURE department_employee;
END emp_dept;
/
CREATE OR REPLACE PACKAGE BODY emp_dept AS
CURSOR dept_employees RETURN EmpDept is (select employee_id , last_name , d.department_id from Employee e join Department d on e.department_id = d.department_id);
rec EmpDept;
PROCEDURE department_employee
is
begin
open dept_employees;
loop
fetch dept_employees into rec;
exit when dept_employees%notfound;
dbms_output.put_line(rpad(rec.employee_id,5,' ') ||rpad(rec.last_name,12,' ') || rec.department_id);
end loop;
end;
END emp_dept;
/
execute EMP_DEPT.DEPARTMENT_EMPLOYEE;
As of package, when you fix errors (wrong column names), it compiles and returns result:
SQL> CREATE OR REPLACE PACKAGE emp_dept AS
2 TYPE empdept IS RECORD (
3 employee_id NUMBER,
4 last_name VARCHAR2(25),
5 department_id NUMBER
6 );
7 CURSOR dept_employees RETURN empdept;
8 PROCEDURE department_employee;
9
10 END emp_dept;
11 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY emp_dept AS
2
3 CURSOR dept_employees RETURN empdept IS
4 ( SELECT
5 emp_id,
6 lastname,
7 d.depid
8 FROM
9 employee e
10 JOIN department d ON e.dep_id = d.depid
11 );
12
13 rec empdept;
14
15 PROCEDURE department_employee IS
16 BEGIN
17 OPEN dept_employees;
18 LOOP
19 FETCH dept_employees INTO rec;
20 EXIT WHEN dept_employees%notfound;
21 dbms_output.put_line(rpad(rec.employee_id, 5, ' ')
22 || rpad(rec.last_name, 12, ' ')
23 || rec.department_id);
24
25 END LOOP;
26
27 END;
28
29 END emp_dept;
30 /
Package body created.
Testing:
SQL> EXECUTE emp_dept.department_employee;
1 samo 1
2 wol 1
3 moh 1
4 imk 2
5 geh 2
PL/SQL procedure successfully completed.
SQL>
As of trigger: that question/requirement doesn't make any sense. How would you enter rows into all those tables, based on 3 columns you enter into the department table? You just don't have any values to insert ... From my point of view, either you misinterpreted the problem, or it can't be done as you described it.

Oracle 11g - how to return record from function with table join

In Oracle 11g , I am trying to return multiple columns from a function call which is joined with a tables. This function takes employee_id as input and should return first_name and last_name as two separate columns from employees table.
I created a type
create or replace
type mytype as object
( val_1 varchar2(100),
val_2 number
);
/
And function
create or replace
function myfunc(p_in number) return mytype is
v_deptname varchar2(100);
v_mgrid number;
begin
select department_name,manager_id into v_deptname,v_mgrid from DEPARTMENTS where department_id = p_in;
return
mytype(v_deptname,v_mgrid);
end;
/
Both got created successfully. But when I execute function ,
select employee_id, salary, myfunc(department_id) from EMPLOYEES where employee_id in(100,101);
It gives result like below,
EMPLOYEE_ID SALARY
----------- ----------
MYFUNC(DEPARTMENT_ID)(VAL_1, VAL_2)
--------------------------------------------------------------------------------
100 24000
MYTYPE('Executive', 100)
101 17000
MYTYPE('Executive', 100)
But i want my result to be like ,
EMPLOYEE_ID SALARY VAL_1 VAL_2
----------- ---------- ------------------------------ ----------
100 24000 Executive 100
101 17000 Executive 100
Please help to achieve this.
Thanks
Well, you might be missing yet another type, based on MYTYPE.
Here's an example (I'm using Scott's schema as I don't have your tables). I've added DEPTNO into MYTYPE so that I'd be able to join the result (returned by the function) with the EMP table.
This is what you have:
SQL> create or replace type mytype as object
2 (deptno number,
3 dname varchar2(20),
4 loc varchar2(20));
5 /
Type created.
This is what you are missing:
SQL> create or replace type mytab as table of mytype;
2 /
Type created.
A function: note line 9:
SQL> create or replace function myfunc (p_in number) return mytab is
2 v_dname varchar2(20);
3 v_loc varchar2(20);
4 begin
5 select dname, loc
6 into v_dname, v_loc
7 from dept
8 where deptno = p_in;
9 return mytab(mytype(p_in, v_dname, v_loc));
10 end myfunc;
11 /
Function created.
Testing:
SQL> select * from table(myfunc(10));
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
SQL>
SQL> select e.ename, e.sal, m.dname, m.loc
2 from emp e join table(myfunc(e.deptno)) m on m.deptno = e.deptno
3 where e.deptno = 10
4 order by m.dname, e.ename;
ENAME SAL DNAME LOC
---------- ---------- -------------------- --------------------
CLARK 2450 ACCOUNTING NEW YORK
KING 10000 ACCOUNTING NEW YORK
MILLER 1300 ACCOUNTING NEW YORK
SQL>
You can try as below:
--Tables
CREATE TABLE dept (
deptno NUMBER,
dname VARCHAR2 (20),
loc VARCHAR2 (20)
);
/
CREATE TABLE employee (
employee_id NUMBER,
salary NUMBER
);
Code:
CREATE OR REPLACE TYPE mytype AS OBJECT (
deptno NUMBER,
dname VARCHAR2 (20),
loc VARCHAR2 (20)
);
CREATE OR REPLACE TYPE mytab AS TABLE OF mytype;
CREATE OR REPLACE FUNCTION myfunc ( p_in NUMBER)
RETURN mytab
IS
v_var mytab := mytab ();
BEGIN
SELECT
mytype (
deptno,
dname,
loc
)
BULK COLLECT INTO
v_var
FROM
dept
WHERE
deptno = p_in;
return (v_var);
END myfunc;
/
Execution:
SELECT
e.*,
t.deptno,
t.dname
FROM
employee e
CROSS JOIN
TABLE ( myfunc (100) ) t ;
Output:
Employee_Id SALARY DEPT DNAME
--------- ------ ---- ------
1001 20000 100 Executive
1002 25000 100 Executive

how to insert data into database with column name from input user

can I insert data into database with column name from input user ?
example:
INSERT INTO res_booking_status
(AirlineCode,
flightnumber,
departuredate,
totalbooking,:#${header[Subclass]})
Values (:#${header[AirlineCode]},
:#${header[FlightNumber]},
:#$ { header[departuredate] },
1,
1 );
I don't know JBoss Fuse, but - as far as Oracle is concerned - you'd use dynamic SQL for such a purpose.
Here's an example based on Scott's schema - I'm inserting a row into the EMP table, into its EMPNO and ENAME columns (which are obligatory). The 3rd and 4th parameters of my procedure are
custom column name
custom value for that column
This is the EMP table:
SQL> desc emp
Name Null? Type
----------------------------------------- -------- ----------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
A procedure:
PAR_EMPNO and PAR_ENAME are obligatory columns' values
PAR_COL1 is custom column name
PAR_VAL1 is custom column's value
L_STR variable contains SQL statement
EXECUTE IMMEDIATE runs dynamic SQL, using parameters you passed:
.
SQL> CREATE OR REPLACE PROCEDURE p_userins (par_empno IN NUMBER,
2 par_ename IN VARCHAR2,
3 par_col1 IN VARCHAR2,
4 par_val1 IN VARCHAR2)
5 IS
6 l_str VARCHAR2 (500);
7 BEGIN
8 l_str :=
9 'insert into emp (empno, ename, '
10 || par_col1
11 || ') values (:a, :b, :c)';
12
13 EXECUTE IMMEDIATE l_str USING par_empno, par_ename, par_val1;
14 END;
15 /
Procedure created.
Testing:
SQL> EXEC p_userins(1, 'LF', 'deptno', 20);
PL/SQL procedure successfully completed.
SQL> SELECT empno, ename, job, sal, deptno FROM emp WHERE empno = 1;
EMPNO ENAME JOB SAL DEPTNO
---------- ---------- --------- ---------- ----------
1 LF 20
SQL>
Seems to be OK.
Now, how will you use it JBoss Fuse, no idea - if you can call a stored procedure, fine - above is the code you might want to adjust.

FORALL INSERT Not Inserting Rows

I have the following code snippet to insert rows into t1 table, however when I execute the procedure no rows are getting populated in t1 table.
CREATE OR REPLACE PROCEDURE my_proc
IS
TYPE rt_t1 IS TABLE OF t1%ROWTYPE
INDEX BY BINARY_INTEGER;
vrt_t1 rt_t1;
TYPE t_emp_no_list IS TABLE OF t1.emp_no%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_emp_name_list IS TABLE OF t1.emp_name%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_loc_name_list IS TABLE OF t1.loc_name%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_hire_date_list IS TABLE OF t1.hire_date%TYPE
INDEX BY BINARY_INTEGER;
l_emp_no t_emp_no_list;
l_emp_name t_emp_name_list;
l_loc_name t_loc_name_list;
l_hire_date t_hire_date_list;
BEGIN
SELECT empno,
ename,
loc,
hiredate
BULK COLLECT INTO l_emp_no,
l_emp_name,
l_loc_name,
l_hire_date
FROM (SELECT empno,
ename,
dept.loc,
emp.hiredate
FROM emp JOIN dept ON emp.deptno = dept.deptno);
FORALL i IN vrt_t1.FIRST .. vrt_t1.LAST
INSERT INTO t1 (emp_no,
emp_name,
loc_name,
hire_date)
VALUES (l_emp_no (i),
l_emp_name (i),
l_loc_name (i),
l_hire_date (i));
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
You should use cursor to achieve your requirement. See below:
CREATE OR REPLACE PROCEDURE my_proc
IS
TYPE rt_t1 IS TABLE OF t1%ROWTYPE
INDEX BY BINARY_INTEGER;
vrt_t1 rt_t1;
cursor cur is
SELECT empno,
ename,
dept.loc,
emp.hiredate
FROM emp JOIN dept ON emp.deptno = dept.deptno;
BEGIN
OPEN cur;
fetch cur BULK COLLECT INTO vrt_t1;
close cur;
FORALL i IN 1 .. vrt_t1.count
INSERT INTO t1
VALUES vrt_t1(i);
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
PS: the above code will work if the number of columns of both the tables are same;
EDIT..In case you are inserting specific rows to a table then you need to create a RECORD to do so. See below example. Your code is failing coz you are trying to inserting multiple collection in one go. In your case first colelction would be an insert and others should be an update.
CREATE TABLE t1
(
emp_no NUMBER,
emp_name VARCHAR2 (30),
loc_name VARCHAR2 (30),
hire_date DATE
);
--------------
SQL> select * from t1;
no rows selected
CREATE OR REPLACE PROCEDURE my_proc
IS
TYPE TBL IS RECORD
(
emp_no number,
emp_name varchar2(100),
loc_name varchar2(100),
hire_date date
);
TYPE t_emp IS TABLE OF TBL INDEX BY PLS_INTEGER;
var_emp_det t_emp;
BEGIN
SELECT ENO,
ENAME,
JOB,
HIREDATE
BULK COLLECT INTO var_emp_det
from emp_sal;
FORALL i IN 1..var_emp_det.count
INSERT INTO t1 (emp_no,
emp_name,
loc_name,
hire_date)
values (var_emp_det(i).emp_no,
var_emp_det(i).emp_name,
var_emp_det(i).loc_name,
var_emp_det(i).hire_date);
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
Output:
SQL> execute my_proc;
PL/SQL procedure successfully completed.
SQL> select * from t1;
EMP_NO EMP_NAME LOC_NAME
---------- ------------------------------ ------------------------------
HIRE_DATE
---------
2 Thomas IT
03-JAN-14

View based on function, correct query treated as bad

This is code:
CREATE TABLE emp_where (where_clause VARCHAR2(4000));
INSERT INTO emp_where (where_clause)
VALUES ('first_name=''KING'' or department_id = 20');
commit;
CREATE OR REPLACE TYPE t_emp_rec AS OBJECT (
EMPLOYEE_ID NUMBER(4),
FIRST_NAME VARCHAR2(10),
JOB_ID VARCHAR2(9),
MANAGER_ID NUMBER(4),
HIRE_DATE DATE,
SALARY NUMBER(7,2),
DEPARTMENT_ID NUMBER(2)
);
/
CREATE OR REPLACE TYPE t_emp_tab AS TABLE OF t_emp_rec;
/
CREATE OR REPLACE FUNCTION emp_fn RETURN t_emp_tab
PIPELINED IS
l_sql VARCHAR2(32767);
l_where VARCHAR2(4000);
TYPE l_cur_type IS REF CURSOR;
l_cur l_cur_type;
l_rec employees%ROWTYPE;
BEGIN
SELECT where_clause INTO l_where FROM emp_where;
l_sql := 'SELECT * FROM employees WHERE ' || l_where;
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur
INTO l_rec;
EXIT WHEN l_cur%NOTFOUND;
PIPE ROW(t_emp_rec(EMPLOYEE_ID => l_rec.EMPLOYEE_ID
,FIRST_NAME => l_rec.FIRST_NAME
,JOB_ID => l_rec.JOB_ID
,MANAGER_ID => l_rec.MANAGER_ID
,hire_date => l_rec.hire_date
,SALARY => l_rec.SALARY
,DEPARTMENT_ID => l_rec.DEPARTMENT_ID));
END LOOP;
RETURN;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20000, SQLERRM || chr(10) || l_sql);
END;
/
CREATE OR REPLACE VIEW emp_vw AS
UPDATE emp_where SET where_clause = 'EMPLOYEE_ID BETWEEN 100 and 200';
COMMIT;
SELECT * FROM emp_vw;
When I execute SELECT * FROM emp_vw; with this clause: EMPLOYEE_ID BETWEEN 100 and 200
Oracle gives me an error
ORA-20000: ORA-06502: PL/SQL: numeric or value error: number precision too large
SELECT * FROM employees WHERE EMPLOYEE_ID BETWEEN 100 and 200
But when I execute query itself (SELECT * FROM employees WHERE EMPLOYEE_ID BETWEEN 100 and 200) there is no error. Another scenario -
when clause is 'deparment_id = 20', executing a view is correct. But when I change '=' to '>' (department_id > 20) - numeric or value error: number precision too large.
Can someone explain me how this is happening?
If I run:
DESCRIBE employees
Then I get the output:
Name Null Type
-------------- -------- ------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
If you compare it to your t_emp_rec object then you will see that most of the object attributes are a smaller size than the table columns.
Change the object to have the same sizes and it should work:
CREATE OR REPLACE TYPE t_emp_rec AS OBJECT (
EMPLOYEE_ID NUMBER(6),
FIRST_NAME VARCHAR2(20),
JOB_ID VARCHAR2(10),
MANAGER_ID NUMBER(6),
HIRE_DATE DATE,
SALARY NUMBER(8,2),
DEPARTMENT_ID NUMBER(4)
);
/

Resources