Insert data into nested table plsql - oracle

Well, I want to insert data from a table into a nested table.
SET SERVEROUTPUT ON;
DECLARE
TYPE employee_type IS TABLE OF employees.salary%TYPE;
emps employee_type;
i NUMBER(5) := 100;
sal employees.salary%TYPE;
BEGIN
LOOP
SELECT salary INTO sal FROM employees WHERE employees.employee_id = i;
emps := employee_type(sal);
i := i + 1;
EXIT WHEN i > 110;
END LOOP;
SYS.DBMS_OUTPUT.PUT_LINE(emps.count);
This is my code but when I do SYS.DBMS_OUTPUT.PUT_LINE(emps.count); I only get 1 but it should be 10 actually

This:
emps := employee_type(sal);
initializes the collection with a single element containing the salary. Every time you do this, you erase the existing collection and replace it with a new one consisting of one element only again.
Instead, initialize the collection once and then extend it element by element:
DECLARE
TYPE employee_type IS TABLE OF employees.salary%TYPE;
emps employee_type := employee_type(); -- initializing the array right away
i NUMBER(5) := 100;
sal employees.salary%TYPE;
BEGIN
LOOP
SELECT salary INTO sal FROM employees WHERE employees.employee_id = i;
emps.EXTEND(1); -- add one element
emps(emps.COUNT) := sal; -- fill that element
i := i + 1;
EXIT WHEN i > 110;
END LOOP;
SYS.DBMS_OUTPUT.PUT_LINE(emps.count);
END;
It is much easier though to bulk collect the values into the array (which also implicitly initialized the array).
DECLARE
TYPE employee_type IS TABLE OF employees.salary%TYPE;
emps employee_type;
i NUMBER(5) := 100;
BEGIN
SELECT salary BULK COLLECT INTO emps
FROM employees
WHERE employee_id BETWEEN i AND 110;
SYS.DBMS_OUTPUT.PUT_LINE(emps.count);
END;

Related

How assigne value from execute immediate into variable

--create table locations_localtab as select * from HR.locations;
SET SERVEROUTPUT ON;
declare
type tLOC_type is table of locations_localtab%rowtype index by binary_integer;
tLOC tLOC_type := tLOC_type();
vPostal_code locations_localtab.postal_code%type;
vCity locations_localtab.city%type;
vLocal varchar2(50);
vSource varchar2(50);
comm long;
begin
select location_id,null,postal_code,city,state_province,country_id
bulk collect into tLOC
from HR.locations;
for ii in (select column_name from (select 'POSTAL_CODE' c1,'CITY' c2, 'COUNTRY_ID' c3 from dual) UNPIVOT (column_name for (name_of_col) in (c1,c2,c3))) loop
for i in 1..tLOC.count loop
comm := 'vSource := tLOC('||i|| ').'||ii.column_name ;
dbms_output.put_line(comm);
execute immediate comm;
-- select city into vLocal from locations_localtab where location_id = tLOC(i).location_id;
dbms_output.put_line('vSOURCE -->'||'.'||vSOURCE);
dbms_output.put_line('vLOCAL -->'||'.'||vLOCAL);
end loop;
end loop;
-- output -->
-- vSource := tLOC(1).POSTAL_CODE;
-- vSource := tLOC(2).POSTAL_CODE;
I would like to make loop by column_name to use this iterator for another loop with table type..
I would like to assign value of tLOC(i).ii.column_name into variable vSource, how can I do this?
I have no idea how I can deal with it.
Thank you in advance for your help.

Oracle PL/SQL : search record type value

I am using Oracle 11G. Lets say I load data into a collection of ROWTYPE.
How can I extract a name or age from the collection emp_tab if I just pass id as the parameter without using any LOOP's?
create table emp (id number, name varchar2(20), age number);
insert into emp values(10,'Tom',20);
insert into emp values(20,'Nicole',30);
commit;
select * from emp;
declare
TYPE emp_t IS TABLE OF emp%ROWTYPE INDEX BY PLS_INTEGER;
emp_tab emp_t;
begin
select id,name,age bulk collect into emp_tab from emp;
for idx in 1..emp_tab.count loop
dbms_output.put_line(emp_tab(idx).name);
end loop;
end;
You'll need to index your collection by emp.id, and to populate it you can't use bulk collect (at least not in 11g - it may be possible in 20c).
declare
type emp_t is table of emp%rowtype index by pls_integer;
emp_tab emp_t;
idx emp.id%type;
begin
for r in (
select id, name, age
from emp
)
loop
emp_tab(r.id).name := r.name;
emp_tab(r.id).age := r.age;
end loop;
idx := emp_tab.first;
while idx is not null loop
dbms_output.put_line(emp_tab(idx).name);
idx := emp_tab.next(idx);
end loop;
end;

PL/SQL FOR LOOP IMPLICIT CURSOR

There are 2 tables EMPLOYEES and DEPARTMENTS with department_id as primary key for DEPARTMENTS and foreign key on EMPLOYEES.
I want to print all the employee names that belong to a particular department. I know it can be easily achieved by JOINS or EXPLICIT cursors.
I thought why not try with FOR loop and a IMPLICIT cursors.
My question is if it is syntactically correct to write INTO like this. If so why is not assigning any values?
DECLARE
emp_dept_id employees.department_id%TYPE;
emp_emp_id employees.employee_id%TYPE;
emp_last_name employees.last_name%TYPE;
dept_dept_id departments.department_id%TYPE;
dept_dept_name departments.department_name%TYPE;
v_count number DEFAULT 0;
BEGIN
FOR i IN (SELECT DISTINCT department_id, department_name
INTO dept_dept_id, dept_dept_name
FROM departments)
LOOP
--v_COUNT := v_COUNT + 1;
DBMS_OUTPUT.PUT_LINE('HELLO'||dept_dept_id||' '||dept_dept_name);
FOR j IN (SELECT employee_id, last_name
INTO emp_emp_id, emp_last_name
FROM employees)
--WHERE department_id=dept_dept_id)
LOOP
DBMS_OUTPUT.PUT_LINE(emp_emp_id||' '||emp_last_name);
v_COUNT := v_COUNT + 1;
END LOOP;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_COUNT);
END;
You don't use INTO with an implicit cursor:
DECLARE
emp_dept_id employees.department_id%TYPE;
emp_emp_id employees.employee_id%TYPE;
emp_last_name employees.last_name%TYPE;
v_count number DEFAULT 0;
BEGIN
FOR i IN (SELECT DISTINCT department_id, department_name
FROM departments)
LOOP
--v_COUNT := v_COUNT + 1;
DBMS_OUTPUT.PUT_LINE('HELLO'||i.department_id||' '||i.department_name);
FOR j IN (SELECT employee_id, last_name
INTO emp_emp_id, emp_last_name
FROM employees)
--WHERE department_id=i.department_id)
LOOP
DBMS_OUTPUT.PUT_LINE(emp_emp_id||' '||emp_last_name);
v_COUNT := v_COUNT + 1;
END LOOP;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_COUNT);
END;
/
Check this page: http://www.techonthenet.com/oracle/loops/cursor_for.php.
I don't think what you're trying to do is valid. Break this up into two steps: the FOR loop and the INTO. You can still have a SELECT in the FOR IN, it just can't be an INTO.

How to initialise a nested table collection which resides inside a PL/SQL Record?

Friends,
Hope you can help.
What I am trying to achieve is to use a collection type(s) that can be accessed either inside and outside of PL/SQL so that an external program can declare a type of this collection and work with it's contents.
The collection will contain some scaler and one composite datatype.
Using the scott schema as an example, the goal is that each record within the collection should contain the department information and within the same record a collection containing the employee information for that department.
I have got the output I require when using PL/SQL associative arrays but they can only be used from with PL/SQL.
When I convert the code to use another type of collection, nested table, I receive a ORA-06531: Reference to uninitialized collection Which is because I haven't initialised the collection held within the record.
Is it possible to achieve this using this design? Or (as I increasing feel!) have I gone down the wrong path?
Two code samples follow.
Firstly the one that works with PL/SQL associative arrays:
DECLARE
TYPE emp_tab_type IS TABLE OF emp%ROWTYPE
INDEX BY BINARY_INTEGER;
TYPE dept_emp_rec IS RECORD (dept_id dept.deptno%TYPE,
dept_name dept.dname%TYPE,
dept_loc dept.loc%TYPE,
emp_data emp_tab_type);
TYPE dept_emp_tab_type IS TABLE OF dept_emp_rec
INDEX BY BINARY_INTEGER;
l_dept_emp_tab dept_emp_tab_type;
CURSOR dept_cur IS
SELECT d.*
FROM dept d
ORDER BY d.deptno;
CURSOR emps_cur (p_dept_id IN NUMBER ) IS
SELECT e.*
FROM emp e
WHERE e.deptno = p_dept_id
ORDER BY e.ename;
j PLS_INTEGER := 1;
k PLS_INTEGER;
BEGIN
FOR dept_rec IN dept_cur
LOOP
-- populate dept data
l_dept_emp_tab(j).dept_id := dept_rec.deptno;
-- other assignment statements
dbms_output.put_line('dept no ' || l_dept_emp_tab(j).dept_id);
-- populate emp data
k := 1;
FOR emp_row_rec IN emps_cur(dept_rec.deptno)
LOOP
l_dept_emp_tab(j).emp_data(k).empno := emp_row_rec.empno;
-- other assignment statements
dbms_output.put_line( l_dept_emp_tab(j).emp_data(k).empno);
k := k + 1;
END LOOP;
j := j + 1;
END LOOP;
END;
This is the example using nested tables that DOESN'T work
DECLARE
TYPE emp_tab_type IS TABLE OF emp%ROWTYPE;
--INDEX BY BINARY_INTEGER;
TYPE dept_emp_rec IS RECORD (dept_id dept.deptno%TYPE,
dept_name dept.dname%TYPE,
dept_loc dept.loc%TYPE,
emp_data emp_tab_type);
TYPE dept_emp_tab_type IS TABLE OF dept_emp_rec;
--INDEX BY BINARY_INTEGER;
l_dept_emp_tab dept_emp_tab_type := dept_emp_tab_type();
CURSOR dept_cur IS
SELECT d.*
FROM dept d
ORDER BY d.deptno;
CURSOR emps_cur (p_dept_id IN NUMBER ) IS
SELECT e.*
FROM emp e
WHERE e.deptno = p_dept_id
ORDER BY e.ename;
j PLS_INTEGER := 1;
k PLS_INTEGER;
BEGIN
FOR dept_rec IN dept_cur
LOOP
l_dept_emp_tab.EXTEND;
-- populate dept data
l_dept_emp_tab(j).dept_id := dept_rec.deptno;
-- other assignment statements
dbms_output.put_line('dept no ' || l_dept_emp_tab(j).dept_id);
-- populate emp data
k := 1;
FOR emp_row_rec IN emps_cur(dept_rec.deptno)
LOOP
l_dept_emp_tab(j).emp_data(k).empno := emp_row_rec.empno;
-- other assignment statements
dbms_output.put_line( l_dept_emp_tab(j).emp_data(k).empno);
k := k + 1;
END LOOP;
j := j + 1;
END LOOP;
END;
I am using Oracle Enterprise Edition 10.2.0.4
Thanks
You are indeed getting the ORA-06531 error because you haven't initialised the collections within each record. To do this, try adding the line
l_dept_emp_tab(j).emp_data := emp_tab_type();
to the other assignments to fields of l_dept_emp_tab(j).
You'll also need to add a call to l_dept_emp_tab(j).emp_data.EXTEND within the inner loop, to make space for the new entry about to be added. Insert this above all the assignments within the inner loop. If you don't add this, you'll get an ORA-06533: Subscript beyond count error.
You seem to be handling the outer nested table type (dept_emp_tab_type) correctly, by calling its constructor (in the DECLARE section) and by calling EXTEND to grow the nested table. All you need to do is to do the same for each instance of the inner nested table type,emp_tab_type .
Here is a different way, this accomplishes everything pretty much within a query (do note it requires the types to be created outside of the block)
http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96624/05_colls.htm
Creation and cleanup of table and types used
/*
CREATE TABLE EMP (ENAME VARCHAR2(50) , DEPTNO NUMBER, empno number);
INSERT INTO EMP VALUES('m1e',1,1);
INSERT INTO EMP VALUES('m2e',1,2);
insert into emp values('m3e',2,3);
INSERT INTO EMP VALUES('m2e',2,4);
insert into emp values('m3e',3,5);
CREATE TABLE DEPT(deptno NUMBER, dname VARCHAR2(50), loc VARCHAR2(50));
INSERT INTO DEPT VALUES(1 ,'portland','tt');
INSERT INTO DEPT VALUES(2 ,'astoria','tt');
INSERT INTO DEPT VALUES(3 ,'eugene','tt');
Creation of types (note this is not within the package/block so that it is available to SQL)
---
drop type emptable force;
DROP TYPE EMP_TAB_TYPE force;
drop type emptable ;
DROP TYPE DEPT_EMP_REC force;
drop type dep_emp_rec_table force;
DROP TABLE DEPT;
drop table emp;
*/
Now create the types outside the package/block so the types are available to SQL
create or replace TYPE emp_tab_type as object (ENAME VARCHAR2(50) , DEPTNO NUMBER);
create or replace type emptable as table of emp_tab_type ;
CREATE OR REPLACE TYPE DEPT_EMP_REC AS OBJECT (
DEPT_ID NUMBER,
dept_name varchar2(50),
dept_loc varchar2(50),
emp_data emptable);
create or replace type dep_emp_rec_table as table of dept_emp_rec;
Now we can directly select the types into the query (note the use of the cast/MULTISET)
SELECT
DEPT_EMP_REC(
deptno,
dname ,
loc ,
CAST(MULTISET(SELECT ENAME, DEPTNO
FROM EMP e
WHERE e.DEPTNO = d.deptno)
AS emptable))
FROM DEPT D ;
/
DEPT_EMP_REC(DEPTNO,DNAME,LOC,CAST(MULTISET(SELECTENAME,DEPTNOFROMEMPEWHEREE.DEPTNO=D.DEPTNO)ASEMPTABLE)) DEPT_EMP_REC(1,'portland','tt',EMPTABLE(EMP_TAB_TYPE('m1e',1),EMP_TAB_TYPE('m2e',1)))
DEPT_EMP_REC(2,'astoria','tt',EMPTABLE(EMP_TAB_TYPE('m3e',2),EMP_TAB_TYPE('m2e',2)))
DEPT_EMP_REC(3,'eugene','tt',EMPTABLE(EMP_TAB_TYPE('m3e',3)))
Now the block is a bit simpler (putting it all together)
set serveroutput on
DECLARE
p_dep_emp_rec_table dep_emp_rec_table;
BEGIN
SELECT
DEPT_EMP_REC(
DEPTNO,
DNAME,
LOC,
CAST( MULTISET
(
SELECT
ENAME,
DEPTNO
FROM EMP E
WHERE E.DEPTNO = D.DEPTNO
) AS EMPTABLE )
)
BULK COLLECT INTO p_dep_emp_rec_table
FROM
DEPT d ;
FOR I IN P_DEP_EMP_REC_TABLE.FIRST..P_DEP_EMP_REC_TABLE.LAST LOOP
DBMS_OUTPUT.PUT_LINE(I || ':' || P_DEP_EMP_REC_TABLE(I).DEPT_ID || '|' || P_DEP_EMP_REC_TABLE(I).DEPT_NAME || '|' || P_DEP_EMP_REC_TABLE(I).DEPT_LOC);
DBMS_OUTPUT.PUT_LINE('-----------------------');
FOR J IN P_DEP_EMP_REC_TABLE(I).EMP_DATA.FIRST..P_DEP_EMP_REC_TABLE(I).EMP_DATA.LAST LOOP
NULL;
dbms_output.put_line(P_DEP_EMP_REC_TABLE(i).emp_data(j).ENAME || '/' || P_DEP_EMP_REC_TABLE(i).emp_data(j).DEPTNO);
end loop;
END LOOP;
END;
anonymous block completed
1:1|portland|tt
-----------------------
m1e/1
m2e/1
2:2|astoria|tt
-----------------------
m3e/2
m2e/2
3:3|eugene|tt
-----------------------
m3e/3

Convert array of records to refcursor

The question is how to return the l_array as refcursor,
Since the interface i am using can handle cursor easily rather than an array of record.
Plz help
create or replace package sample
TYPE r_type is record( code number; description varchar2(50));
TYPE tr_type IS TABLE OF r_type; l_rarray tr_type ; ind number:=0;
PROCEDURE getdata() IS
CURSOR cur IS
SELECT empid, empname, place, location FROM emp;
TYPE epmid_aat IS TABLE OF emp.empid%TYPE INDEX BY BINARY_INTEGER;
l_empid empid_aat;
BEGIN
k := 1;
FOR j IN (SELECT DISTINCT empid FROM emp)
LOOP
l_empid(k) := j.empid;
k := k + 1;
END LOOP;
FOR i IN cur
LOOP
FOR k IN l_empid.first .. l_empid.last
LOOP
IF l_empid(k) = i.empid THEN
procedure2(i.emp_id);
END IF;
END LOOP;
END LOOP;
END getdata();
PROCEDURE procedure2
(
empid_in IN NUMBER,
description_in IN VARCHAR2(20)
) IS
BEGIN
lrec.code := empid_in;
lrec.description := description_in;
l_rarray(ind) := lrec;
ind := ind + 1;
END procedure2;
end;
I think it should be like this :
OPEN YourRefCursor
FOR SELECT * FROM TABLE (Cast(l_rarray AS tr_type));
Something like this.
TYPE r_type is record ( code number;
description varchar2(50)
);
TYPE tr_type IS TABLE OF r_type;
l_rarray tr_type ;
SELECT r_type(empid, empname)
BULK COLLECT INTO l_rarray
FROM emp;
OPEN YourRefCursor
SELECT *
FROM TABLE (Cast(l_rarray AS r_type));

Resources