Bulk collect into multiple columns - oracle

I can define a one-column table and bulk collect into it. i.e:
create type table_of_strings as table of varchar2(200);
DECLARE
l_tab table_of_strings;
BEGIN
select emp_name bulk collect into l_tab from emp;
END;
But how do I collect into multi-column tables? Say:
create type emp_row as object (emp_name varchar2(200), emp_salary Number);
create type emp_table as table of emp_row ;
DECLARE
l_tab emp_table ;
BEGIN
-- I have tried things like this but would fail:
select (emp_name, emp_salary) bulk collect into l_tab from emp;
select emp_name, emp_salary bulk collect into l_tab from emp;
select * bulk collect into l_tab from (
select emp_name, emp_salary from emp);
END;
Thank you in advance!
Peter

Like this:
select emp_row(emp_name, emp_salary) bulk collect into l_tab from emp;
Here's a slightly modified example based on Scott's schema.
SQL> CREATE TYPE emp_row AS OBJECT (emp_name VARCHAR2 (200), emp_salary NUMBER);
2 /
Type created.
SQL> CREATE TYPE emp_table AS TABLE OF emp_row;
2 /
Type created.
SQL> SET SERVEROUTPUT ON
PL/SQL code:
SQL> DECLARE
2 l_tab emp_table;
3 l_max_sal NUMBER;
4 BEGIN
5 SELECT emp_row (ename, sal)
6 BULK COLLECT INTO l_tab
7 FROM emp
8 WHERE deptno = 10;
9
10 FOR i IN l_tab.FIRST .. l_tab.LAST
11 LOOP
12 DBMS_OUTPUT.put_line (
13 l_tab (i).emp_name || ' - ' || l_tab (i).emp_salary);
14 END LOOP;
15
16 SELECT MAX (emp_salary) INTO l_max_sal FROM TABLE (l_tab);
17
18 DBMS_OUTPUT.put_line ('Max salary = ' || l_max_sal);
19 END;
20 /
CLARK - 2450
KING - 5000
MILLER - 1300
Max salary = 5000
PL/SQL procedure successfully completed.
SQL>

Related

How to return more than one select queries in same procedure

I would like to ask how can i print output in procedure more than one statement.
Assume that you want to show dba_objects and segments row count. But i can not use dbms_sql.return_result my version is 11g.
Something like,
create or replace procedure get_rows_count
(
cursor1 out SYS_REFCURSOR,
cursor2 out SYS_REFCURSOR
)
as
begin
open cursor1 for select count(*) from dba_objects;
open cursor2 for select count(*) from dba_segments;
end get_rows_count;
/
Assume that you want to show dba_objects and segments row count
I assumed it. Conclusion: that's not the way to do it. If you want to get row count from e.g. dba_objects, then you should just
select count(*) from dba_objects;
in any variation you want (pure SQL, function that returns that number, procedure with an OUT parameter (worse option), ...). But, creating a procedure which uses ref cursor for that purpose is ... well, wrong.
If I got you wrong, then: procedure you wrote is OK. You can call it from another PL/SQL procedure (named or anonymous), fetch result into a variable and do something with it (e.g. display it).
Your procedure (selects from Scott's tables; I don't have access to DBA_ views):
SQL> CREATE OR REPLACE PROCEDURE get_rows_count (cursor1 OUT SYS_REFCURSOR,
2 cursor2 OUT SYS_REFCURSOR)
3 AS
4 BEGIN
5 OPEN cursor1 FOR SELECT * FROM emp;
6
7 OPEN cursor2 FOR SELECT * FROM dept;
8 END get_rows_count;
9 /
Procedure created.
How to call it? See line #8:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 rc1 SYS_REFCURSOR;
3 rc2 SYS_REFCURSOR;
4 --
5 rw1 emp%ROWTYPE;
6 rw2 dept%ROWTYPE;
7 BEGIN
8 get_rows_count (rc1, rc2);
9
10 DBMS_OUTPUT.put_line ('Employees -----------');
11
12 LOOP
13 FETCH rc1 INTO rw1;
14
15 EXIT WHEN rc1%NOTFOUND;
16
17 DBMS_OUTPUT.put_line (rw1.ename);
18 END LOOP;
19
20 --
21 DBMS_OUTPUT.put_line ('Departments ---------');
22
23 LOOP
24 FETCH rc2 INTO rw2;
25
26 EXIT WHEN rc2%NOTFOUND;
27
28 DBMS_OUTPUT.put_line (rw2.dname);
29 END LOOP;
30
31 DBMS_OUTPUT.put_line ('First ref cursor: ' || rc1%ROWCOUNT);
32 DBMS_OUTPUT.put_line ('Second ref cursor: ' || rc2%ROWCOUNT);
33 END;
34 /
Result:
Employees -----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
Departments ---------
ACCOUNTING
RESEARCH
SALES
OPERATIONS
First ref cursor: 14
Second ref cursor: 4
PL/SQL procedure successfully completed.
SQL>
You can use famous DBMS_OUTPUT.PUT_LINE() along with %ROWCOUNT suffix for your case such as
SET serveroutput ON
CREATE OR REPLACE PROCEDURE get_rows_count(
cursor1 OUT SYS_REFCURSOR,
cursor2 OUT SYS_REFCURSOR,
count1 OUT INT,
count2 OUT INT
) AS
cur_rec_obj user_objects%ROWTYPE;
cur_rec_seg user_segments%ROWTYPE;
BEGIN
OPEN cursor1 FOR SELECT * FROM user_objects;
LOOP
FETCH cursor1 INTO cur_rec_obj;
EXIT WHEN cursor1%NOTFOUND;
END LOOP;
OPEN cursor2 FOR SELECT * FROM user_segments;
LOOP
FETCH cursor2 INTO cur_rec_seg;
EXIT WHEN cursor2%NOTFOUND;
END LOOP;
count1 := cursor1%ROWCOUNT;
count2 := cursor2%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE(count1);
DBMS_OUTPUT.PUT_LINE(count2);
END;
/
and you can call as follows from the SQL Window of PL/SQL Developer :
DECLARE
v_cursor1 SYS_REFCURSOR;
v_cursor2 SYS_REFCURSOR;
v_count1 INT;
v_count2 INT;
BEGIN
get_rows_count(v_cursor1, v_cursor2, v_count1, v_count2 );
END;
/

Need to fetch the table details using stored procedure when we give table name as input

CREATE TABLE test_table
(
col1 NUMBER(10),
col2 NUMBER(10)
);
INSERT INTO test_table
VALUES (1, 2);
I am writing a stored procedure wherein if I give a table name as an input, that should give me the table data and column details.
For example:
SELECT *
FROM <input_table_name>;
But this causes an error that the SQL command has not ended properly even though I have taken care of this.
My attempt:
CREATE OR REPLACE PROCEDURE sp_test(iv_table_name IN VARCHAR2,
p_out_cur OUT SYS_REFCURSOR)
AS
lv_str VARCHAR2(400);
lv_count NUMBER(1);
lv_table_name VARCHAR2(255):=UPPER(iv_table_name);
BEGIN
lv_str := 'SELECT * FROM '||lv_table_name;
SELECT COUNT(1) INTO lv_count FROM all_tables WHERE table_name = lv_table_name;
IF lv_count = 0 THEN
dbms_output.put_line('Table does not exist');
ELSE
OPEN p_out_cur FOR lv_str;
END IF;
END sp_test;
Tool used: SQL developer(18c)
In dynamic SQL, you do NOT terminate statement with a semicolon.
EXECUTE IMMEDIATE 'SELECT * FROM '||lv_table_name||';';
-----
remove this
Anyway, you won't get any result when you run that piece of code. If you really want to see table's contents, you'll have to switch to something else, e.g. create a function that returns ref cursor.
Sample data:
SQL> SELECT * FROM test_table;
COL1 COL2
---------- ----------
1 2
3 4
Procedure you wrote is now correct:
SQL> CREATE OR REPLACE PROCEDURE sp_test (iv_table_name IN VARCHAR2,
2 p_out_cur OUT SYS_REFCURSOR)
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 BEGIN
8 lv_str := 'SELECT * FROM ' || lv_table_name;
9
10 SELECT COUNT (1)
11 INTO lv_count
12 FROM all_tables
13 WHERE table_name = lv_table_name;
14
15 IF lv_count = 0
16 THEN
17 DBMS_OUTPUT.put_line ('Table does not exist');
18 ELSE
19 OPEN p_out_cur FOR lv_str;
20 END IF;
21 END sp_test;
22 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_rc SYS_REFCURSOR;
3 l_col1 NUMBER (10);
4 l_col2 NUMBER (10);
5 BEGIN
6 sp_test ('TEST_TABLE', l_rc);
7
8 LOOP
9 FETCH l_rc INTO l_col1, l_col2;
10
11 EXIT WHEN l_rc%NOTFOUND;
12
13 DBMS_OUTPUT.put_line (l_col1 || ', ' || l_col2);
14 END LOOP;
15 END;
16 /
1, 2 --> contents of the
3, 4 --> TEST_TABLE
PL/SQL procedure successfully completed.
SQL>
A function (instead of a procedure with the OUT parameter):
SQL> CREATE OR REPLACE FUNCTION sf_test (iv_table_name IN VARCHAR2)
2 RETURN SYS_REFCURSOR
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 l_rc SYS_REFCURSOR;
8 BEGIN
9 lv_str := 'SELECT * FROM ' || lv_table_name;
10
11 SELECT COUNT (1)
12 INTO lv_count
13 FROM all_tables
14 WHERE table_name = lv_table_name;
15
16 IF lv_count = 0
17 THEN
18 raise_application_error (-20000, 'Table does not exist');
19 ELSE
20 OPEN l_rc FOR lv_str;
21 END IF;
22
23 RETURN l_rc;
24 END sf_test;
25 /
Function created.
Testing:
SQL> SELECT sf_test ('liksajfla') FROM DUAL;
SELECT sf_test ('liksajfla') FROM DUAL
*
ERROR at line 1:
ORA-20000: Table does not exist
ORA-06512: at "SCOTT.SF_TEST", line 18
SQL> SELECT sf_test ('TEST_TABLE') FROM DUAL;
SF_TEST('TEST_TABLE'
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
COL1 COL2
---------- ----------
1 2
3 4
SQL>

Error when calling procedure from procedure

The following procedure compilation error occurred:
Procedure A receives the result from table B, inputs it to the GLOBAL TEMPORARY TABLE, and retrieves the final result after the operation.
Procedure B is a function that manipulates and retrieves the source data.
When I run the A procedure, I get the following compilation error:
/* GLOBAL TEMPORARY TABLE */
CREATE GLOBAL TEMPORARY TABLE TT_TB_TMP
(
TABLE_NAME VARCHAR2(200)
,COLUMN_NAME VARCHAR2(200)
)
ON COMMIT DELETE ROWS
NOPARALLEL;
/* PROCEDURE B(SP_TEST_H2)*/
create or replace PROCEDURE SP_TEST_H2
(
p_TBL_NAME IN VARCHAR
)
AS
v_cursor SYS_REFCURSOR;
BEGIN
OPEN v_cursor FOR
SELECT TABLE_NAME, COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME = 'ALL_XML_SCHEMAS'; -- p_TBL_NAME
DBMS_SQL.RETURN_RESULT(v_cursor);
END SP_TEST_H2;
/* PROCEDURE A(SP_TEST_H1)*/
create or replace PROCEDURE SP_TEST_H1
(
p_TBL_NAME IN VARCHAR
)
AS
v_cursor SYS_REFCURSOR;
BEGIN
DECLARE
cv_ins SYS_REFCURSOR;
v_temp TT_TB_TMP%ROWTYPE;
BEGIN
cv_ins := SP_TEST_H2('XXX');
LOOP
FETCH cv_ins INTO v_temp;
EXIT WHEN cv_ins%NOTFOUND;
INSERT INTO TT_TB_TMP VALUES v_temp;
END LOOP;
CLOSE cv_ins;
/*
OPEN v_cursor FOR
SELECT * FROM TT_TB_TMP;
DBMS_SQL.RETURN_RESULT(v_cursor);
*/
END;
END SP_TEST_H1
PLS-00222: Function with name 'SP_TEST_H2' does not exist in scope
What did I do wrong?
If you're returning something, then use a function - they are designed for such a purpose.
That's what Oracle told you:
Function with name 'SP_TEST_H2' does not exist in scope
which is related to this line in your code:
cv_ins := SP_TEST_H2('XXX');
Function:
SQL> CREATE OR REPLACE FUNCTION sf_test_h2 (p_tbl_name IN VARCHAR)
2 RETURN SYS_REFCURSOR
3 AS
4 v_cursor SYS_REFCURSOR;
5 BEGIN
6 OPEN v_cursor FOR SELECT table_name, column_name
7 FROM all_tab_columns
8 WHERE table_name = p_tbl_name;
9
10 RETURN v_cursor;
11 END sf_test_h2;
12 /
Function created.
Procedure:
SQL> CREATE OR REPLACE PROCEDURE sp_test_h1 (p_tbl_name IN VARCHAR)
2 AS
3 cv_ins SYS_REFCURSOR;
4 v_temp tt_tb_tmp%ROWTYPE;
5 BEGIN
6 cv_ins := sf_test_h2 (p_tbl_name);
7
8 LOOP
9 FETCH cv_ins INTO v_temp;
10
11 EXIT WHEN cv_ins%NOTFOUND;
12
13 INSERT INTO tt_tb_tmp (table_name, column_name)
14 VALUES (v_temp.table_name, v_temp.column_name);
15 END LOOP;
16
17 CLOSE cv_ins;
18 END sp_test_h1;
19 /
Procedure created.
Testing:
SQL> EXEC sp_test_h1('DEPT');
PL/SQL procedure successfully completed.
SQL> SELECT * FROM tt_tb_tmp;
TABLE_NAME COLUMN_NAME
-------------------- --------------------
DEPT LOC
DEPT DNAME
DEPT DEPTNO
SQL>

Oracle PL/SQL Return recordset and specific column(from recordset) in seperate variable

I need to return value from procedure, recordset and single value(extract from recordset)
CREATE TABLE EMP
(
EMP_ID number;
EMP_NAME varchar2(20);
EMP_FLAG char(1);
);
CREATE TABLE SALARY
(
EMP_ID number;
EMP_SAL number;
);
BEGIN
insert into EMP values(10,'John','N');
insert into EMP values(20,'May','Y');
insert into SALARY values(10,10000);
insert into SALARY values(10,25000);
END;
/
Here is my Procedure
CREATE OR REPLACE PROCEDURE TEST(Flag in char(1),curEMP out SYS_REFCURSOR,Sal out number)
v_sal number;
v_emp_id number;
v_name varchar2(20);
AS
open curEMP for
select e.emp_id
,e.emp_name
,s.sal
from emp e
left join sal s on e.emp_id = s.emp_id
where e.emp_flag = Flag;
loop
fetch curEMP into v_emp_id,v_name,v_Sal; --in real life there are more than 20+ fields
exit when curEMP%notfound;
end loop;
Sal := v_sal;
END TEST;
/
Script to call Procedure
set serveroutput on;
variable cr refcursor;
variable sal number;
exec TEST('Y',:cr,:sal);
print cr;
print sal;
Here is the output
Output
sal
25000
However, I need output like this
--Output from cursor list
emp_id emp_name sal
20 May 25000
--from sal variable
sal
25000
There are multiple issues with your table DDL and procedure, but I guess you have executed the procedure anyway.
To fetch all the records, You can use the only cursor and open it in the procedure, there is no need for looping through a cursor in your procedure.
see the procedure and its call in the following example:
SQL> CREATE OR REPLACE PROCEDURE TEST (
2 FLAG IN CHAR, -- size can not be given in the input parameter
3 CUREMP OUT SYS_REFCURSOR
4 --SAL OUT NUMBER -- no need of this parameter
5 ) AS
6 --V_SAL NUMBER; -- all variables are not needed
7 --V_EMP_ID NUMBER;
8 --V_NAME VARCHAR2(20);
9 BEGIN
10 OPEN CUREMP FOR SELECT
11 E.EMP_ID,
12 E.EMP_NAME,
13 S.EMP_SAL
14 FROM
15 EMP E
16 LEFT JOIN SALARY S ON E.EMP_ID = S.EMP_ID
17 WHERE E.EMP_FLAG = FLAG;
18 -- LOOP -- looping is not needed
19 -- FETCH CUREMP INTO
20 -- V_EMP_ID,
21 -- V_NAME,
22 -- V_SAL; --in real life there are more than 20+ fields
23 -- EXIT WHEN CUREMP%NOTFOUND;
24 -- END LOOP;
25
26 -- SAL := V_SAL;
27 END TEST;
28 /
Procedure created.
SQL>
Testing the procedure now.
SQL> VAR x REFCURSOR;
SQL> exec test('Y',:x);
PL/SQL procedure successfully completed.
SQL> print :x
EMP_ID EMP_NAME EMP_SAL
---------- -------------------- ----------
20 May 25000
SQL>
Cheers!!

How to run a query on different schemes at once

I want to run a query on different schemes to get data and export it. I use the following code
DECLARE
sql_statment VARCHAR2(2000);
BEGIN
FOR c IN (SELECT brchcode FROM brchs) LOOP
sql_statment := 'select distinct ''' || c.brchcode ||''', t.risuid from ' || c.brchcode ||
'.reg_individualacnt_detail t
where t.historytypecode = 60';
EXECUTE IMMEDIATE sql_statment;
END LOOP;
END;
where brchcode is the name of different schemes
I can't see any output. what can I do?
Code you wrote can't work as you have to return the result into something; it is PL/SQL and requires an INTO clause. As you chose to return two values (columns) and multiple rows, that can't be a scalar variable; you could pick ref cursor or a collection, for example.
Here's an example which shows one option.
I'll be using two schemas: SCOTT (current schema) and HR. Both will be having the DEPT table.
As Scott already has it, I'll create one in HR schema and grant access to Scott (otherwise, Scott won't even see it and the procedure (i.e. the function) will fail):
SQL> connect hr/hr
Connected.
SQL> create table dept (deptno number, dname varchar2(10), loc varchar2(10));
Table created.
SQL> insert into dept values (55, 'IT', 'Zagreb');
1 row created.
SQL> grant select on dept to scott;
Grant succeeded.
SQL> commit;
Commit complete.
Back to Scott, to create a table (which contains schema names I'll be selecting from) and a function. I chose to return REF CURSOR; you could return something else, if you want.
SQL> connect scott/tiger
Connected.
SQL> create table brchs (brchcode varchar2(10));
Table created.
SQL> insert into brchs (brchcode)
2 select 'scott' from dual union all
3 select 'hr' from dual;
2 rows created.
SQL> create or replace function f_br
2 return sys_refcursor
3 is
4 l_str varchar2(4000);
5 l_rc sys_refcursor;
6 begin
7 for cur_r in (select brchcode from brchs) loop
8 l_str := l_str ||
9 'union all
10 select ' || chr(39)|| cur_r.brchcode ||chr(39) || ', d.dname
11 from ' || cur_r.brchcode ||'.dept d
12 where d.deptno > 0';
13 end loop;
14
15 l_str := ltrim(l_str, 'union all');
16
17 open l_rc for l_str;
18 return l_rc;
19 end;
20 /
Function created.
SQL>
Finally, testing:
SQL> select f_br from dual;
F_BR
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'SCOT DNAME
----- --------------
scott ACCOUNTING
scott RESEARCH
scott SALES
scott OPERATIONS
hr IT
SQL>

Resources