How to display sys_refcursor output pl sql with Toad tool - oracle

I have written a query to execute the SP. The execution works fine with this. However, I'm not able to see output results.
declare v_rc sys_refcursor;
begin
SUSER.TRANS_REP (v_rc ,'Investments Series','31-12-2012','Dealer Group','All Adv') ;
end;
How to display output result with sys_refcursor. Please help.
note: I tried to print cursor but did not get any help. Also refered this (How to display a sys_refcursor data in TOAD's DataGrid and https://community.oracle.com/thread/627571), but still no help.

In SQL*Plus you could easily do it using a refcursor variable.
SQL> var r refcursor
SQL>
SQL> BEGIN
2 OPEN :r FOR SELECT empno,ename FROM emp;
3 END;
4 /
PL/SQL procedure successfully completed.
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
EMPNO ENAME
---------- ----------
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
SQL>
I guess in TOAD, you have some sort of output options. Select the variables you want to see the values in the output, the ref cursor result set would open in a different window.

Related

How to make a PL/SQL stored procedure display a table?

Lets say I have a table called student that has 3 attributes, the student number, name and the favorite color and when i call the procedure like this:
execute favColor('RED');
the output should be:
NAME FAV_COLOR
-------- -----------
MARY RED
JANE RED
PETER RED
JOHN RED
Can I have an example code to do this? So far I only see procedures inserting or updating records and if there is a select statement in the procedure, it must have an into clause, does that mean it is impossible for procedures to display this output?
As I don't have your table, I'll use Scott's with jobs instead of colors.
Sample data:
SQL> select ename, job from emp order by job, ename;
ENAME JOB
---------- ---------
FORD ANALYST
SCOTT ANALYST
ADAMS CLERK
JAMES CLERK
MILLER CLERK
SMITH CLERK
BLAKE MANAGER
CLARK MANAGER
JONES MANAGER
KING PRESIDENT
ALLEN SALESMAN
MARTIN SALESMAN
TURNER SALESMAN
WARD SALESMAN
14 rows selected.
Procedure will return employees who have certain job:
SQL> create or replace procedure favJob (par_job in varchar2, par_rc out sys_refcursor)
2 is
3 begin
4 open par_rc for
5 select ename, job
6 from emp
7 where job = par_job
8 order by ename;
9 end;
10 /
Procedure created.
Testing:
SQL> var rc refcursor
SQL> exec favJob('MANAGER', :rc);
PL/SQL procedure successfully completed.
SQL> print rc
ENAME JOB
---------- ---------
BLAKE MANAGER
CLARK MANAGER
JONES MANAGER
SQL>

Monitor Table for duplicate inserts in Oracle 11g

We have a table in Oracle 11g DB which has millions of records. For a few months, we have noticed that duplicate records are getting inserted into the table. Is there any way to alert via email when the duplicate records get inserted into the table?
We are in the process of creating a unique index for the table, but it will take sometime.
Meanwhile, can an email-alert be created to notify us when duplicate records are getting inserted?
We have OEM installed to monitor this Oracle DB.
If selecting from table takes a long time, I guess you don't want to intercept duplicates as they happen because every insert/update would kill performance even more than it does now.
Therefore, if table isn't in use 24/7, perhaps you could schedule that check and either notify someone or - even better - take action immediately.
That would be a stored procedure, e.g.
create or replace procedure p_del_dup as
begin
delete from your_table a
where a.rowid > (select min(b.rowid)
from your_table b
where b.future_unique_column = a.future_unique_column
);
end;
Live example, based on Scott's sample schema. I'll create a table that contains EMP data, enter some duplicates and delete them.
SQL> create table test as select * from emp where deptno in (10, 20);
Table created.
SQL> create or replace procedure p_del_dup as
2 begin
3 delete from test a
4 where a.rowid > (select min(b.rowid)
5 from test b
6 where b.empno = a.empno --> column(s) which will enforce uniqueness
7 );
8 end;
9 /
Procedure created.
Create a database job; schedule it to run at 02:00 every night
SQL> declare
2 x number;
3 begin
4 dbms_job.submit
5 ( job => x
6 ,what => 'p_del_dup;'
7 ,next_date => to_date('07.04.2020 02:00:00','dd/mm/yyyy hh24:mi:ss')
8 ,interval => 'TRUNC (SYSDATE+1) + 2 / 24'
9 ,no_parse => false
10 );
11 dbms_output.put_line('Job Number is: ' || to_char(x));
12 commit;
13 end;
14 /
Job Number is: 104
PL/SQL procedure successfully completed.
OK, let's now insert some duplicates (all from deptno = 10):
SQL> insert into test select * from emp where deptno = 10;
3 rows created.
SQL> select * from test order by deptno, ename;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 5000 10
7839 KING PRESIDENT 17.11.81 5000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
7934 MILLER CLERK 7782 23.01.82 1300 10
7876 ADAMS CLERK 7788 12.01.83 1100 20
7902 FORD ANALYST 7566 03.12.81 3000 20
7566 JONES MANAGER 7839 02.04.81 2975 20
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7369 SMITH CLERK 7902 17.12.80 800 20
11 rows selected.
I won't wait until 02:00 so I'll run the job manually. Remember, its ID is 104?
SQL> exec dbms_job.run(104);
PL/SQL procedure successfully completed.
SQL> select * from test order by deptno, ename;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 5000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
7876 ADAMS CLERK 7788 12.01.83 1100 20
7902 FORD ANALYST 7566 03.12.81 3000 20
7566 JONES MANAGER 7839 02.04.81 2975 20
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7369 SMITH CLERK 7902 17.12.80 800 20
8 rows selected.
SQL>
OK, duplicates are silently deleted.
If you want, you can modify the procedure and - using UTL_MAIL package - send an e-mail message to someone. If number of rows deleted is enough, you'd send SQL%ROWCOUNT number. Or, send whatever you want.

How can I output the result of a PL/SQL script to a grid?

I'm trying to figure out how I can output the results of a PL/SQL script to a results grid the same way results from a simple query are output. I'm new to Oracle from SQL Server, so I might be overlooking something very basic. For example how would I view the results of something like the following simple T-SQL (SQL Server's PL) script? Is there some sort of print_to_grid function? Or perhaps using a cursor? I know about dbms_output, but I definitely don't want to have to concatenate a string out of every script I want to see results from.
declare #emps table(employeeId int, name varchar(40)
insert into #emps
select employeeID, name
from employees
select * from #emps
This seems like it should be the simplest thing in the world, but I can't find a straight answer for it. Any help or tool recommendations would be greatly appreciated.
It sounds like you are after a ref cursor. For example
SQL> variable r refcursor
SQL>
SQL> begin
2 open :r for
3 select empno, ename
4 from scott.emp;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
At the point you open that refcursor, the result is pre-ordained - so there is no need to worry about read locks or data consistency etc. For example, I'll open the ref cursor when there is rows in the table, and then delete them...my printed data is still as it was at cursor open time.
SQL>
SQL>
SQL>
SQL> begin
2 open :r for
3 select empno, ename
4 from scott.emp;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
SQL> delete from scott.emp;
14 rows deleted.
SQL>
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
SQL>
SQL>
SQL> rollback;
Rollback complete.
SQL>

Oracle PL/SQL collect values from a loop into a cursor

I have a PL/SQL TABLE TYPE result set that contains document ids.
I can loop over the result set without a problem, but the issue is that I have to return a sys_refcursor from the function, but I am unable to collect the values from the loop into the cursor.
TYPE table_typ IS TABLE OF DOCUMENT_QUEUE.ENV_ID%TYPE INDEX BY PLS_INTEGER;
FUNCTION GET_DOCS()
RETURN SYS_REFCURSOR
IS
LS_CUR SYS_REFCURSOR;
LR_UPDATED_ROWS table_typ;
BEGIN
UPDATE DOCUMENT_QUEUE DQ
...
RETURNING DQ.ENV_ID BULK COLLECT INTO LR_UPDATED_ROWS;
-- Need to collect all of the following rows into the cursor
FOR indx IN NVL (LR_UPDATED_ROWS.FIRST, 0) .. NVL (LR_UPDATED_ROWS.LAST, -1)
LOOP
SELECT * FROM DOCUMENT_QUEUE DQ WHERE DQ.ENV_ID = LR_UPDATED_ROWS(indx);
END LOOP;
RETURN LS_CUR;
END GET_DOCS;
All help and hints are welcome.
FOR indx IN NVL (LR_UPDATED_ROWS.FIRST, 0) .. NVL (LR_UPDATED_ROWS.LAST, -1)
LOOP
SELECT * FROM DOCUMENT_QUEUE DQ WHERE DQ.ENV_ID = LR_UPDATED_ROWS(indx);
END LOOP;
RETURN LS_CUR;
You do not need the cursor FOR LOOP. You could use OPEN CURSOR FOR statement and return a SYS_REFCURSOR.
For example,
OPEN LS_CUR FOR SELECT * FROM DOCUMENT_QUEUE DQ
WHERE DQ.ENV_ID IN (SELECT * FROM TABLE(LR_UPDATED_ROWS));
RETURN LS_CUR;
or,
OPEN LS_CUR FOR SELECT * FROM DOCUMENT_QUEUE DQ
WHERE DQ.ENV_ID MEMBER OF LR_UPDATED_ROWS;
RETURN LS_CUR;
However, in order to do that, you must CREATE the type at SQL level not at PL/SQL level. Else, you would receive PLS-00642: local collection types not allowed in SQL statements.
A small demo:
Create the type at SQL level:
SQL> CREATE OR REPLACE TYPE table_typ AS TABLE OF NUMBER
2 /
Type created.
Let's get the output in SQL*Plus using a refcursor:
Using MEMBER OF syntax:
SQL> variable r refcursor
SQL> DECLARE
2 l_typ table_typ;
3 TYPE numbers IS TABLE OF NUMBER;
4 n numbers;
5 BEGIN
6 SELECT empno BULK COLLECT INTO l_typ FROM emp;
7 OPEN :r FOR SELECT empno,
8 ename FROM emp WHERE empno member OF l_typ;
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
Using TABLE function:
SQL> variable r refcursor
SQL> DECLARE
2 l_typ table_typ;
3 TYPE numbers IS TABLE OF NUMBER;
4 n numbers;
5 BEGIN
6 SELECT empno BULK COLLECT INTO l_typ FROM emp;
7 OPEN :r FOR SELECT empno,
8 ename FROM emp WHERE empno IN (SELECT * from TABLE(l_typ));
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
For the mentioned requirement i have mentioned below a snippet which will help to fetch all the rows into ref cursor for every rowid. Let me know if this helps.
CREATE OR REPLACE TYPE table_typ
IS
TABLE OF DOCUMENT_QUEUE.ENV_ID%TYPE INDEX BY PLS_INTEGER;
CREATE OR REPLACE FUNCTION GET_DOCS
RETURN SYS_REFCURSOR
IS
LS_CUR SYS_REFCURSOR;
LR_UPDATED_ROWS table_typ;
lv_rows_lst VARCHAR2(32676);
BEGIN
SELECT <COL1> BULK COLLECT INTO LR_UPDATED_ROWS FROM <TABLE_NAME>;
FOR I IN LR_UPDATED_ROWS.FIRST..LR_UPDATED_ROWS.LAST
LOOP
lv_rows_lst:=lv_rows_lst||','||LR_UPDATED_ROWS(I);
END LOOP;
lv_rows_lst:=SUBSTR(lv_rows_lst,2,LENGTH(lv_rows_lst));
OPEN LS_CUR FOR 'SELECT * FROM DOCUMENT_QUEUE DQ WHERE DQ.ENV_ID IN ('||lv_rows_lst||')';
RETURN LS_CUR;
END GET_DOCS;

What is the equivalent of SQL Server APPLY in Oracle?

I am new to Oracle. Is there a builtin keyword does the same job of SQL Server APPLY?
I think the equivalent of the APPLY clause in Oracle is called a lateral JOIN. A lateral join in Oracle is when you join a table A with a function F that outputs rows and this function has columns of A as parameters.
Let's build a small example with this setup:
SQL> CREATE OR REPLACE TYPE emp_row AS OBJECT (
2 empno NUMBER(4),
3 ename VARCHAR(10),
4 job VARCHAR(9),
5 deptno NUMBER(2)
6 );
7 /
Type created
SQL> CREATE OR REPLACE TYPE emp_tab AS TABLE OF emp_row;
2 /
Type created
SQL> CREATE OR REPLACE FUNCTION get_emp_dept(p_deptno NUMBER) RETURN emp_tab IS
2 l_result emp_tab;
3 BEGIN
4 SELECT emp_row(empno, ename, job, deptno)
5 BULK COLLECT INTO l_result
6 FROM emp
7 WHERE deptno = p_deptno;
8 RETURN l_result;
9 END get_emp_dept;
10 /
Function created
A lateral join is automatic in Oracle, there is no special keyword:
SQL> SELECT dept.dname, emp.empno, emp.ename, emp.job
2 FROM dept
3 CROSS JOIN TABLE(get_emp_dept(dept.deptno)) emp;
DNAME EMPNO ENAME JOB
-------------- ----- ---------- ---------
ACCOUNTING 7782 CLARK MANAGER
ACCOUNTING 7839 KING PRESIDENT
ACCOUNTING 7934 MILLER CLERK
RESEARCH 7369 SMITH CLERK
RESEARCH 7566 JONES MANAGER
RESEARCH 7788 SCOTT ANALYST
RESEARCH 7876 ADAMS CLERK
RESEARCH 7902 FORD ANALYST
SALES 7499 ALLEN SALESMAN
SALES 7521 WARD SALESMAN
SALES 7654 MARTIN SALESMAN
SALES 7698 BLAKE MANAGER
SALES 7844 TURNER SALESMAN
SALES 7900 JAMES CLERK
14 rows selected
In Oracle we can use a pipelined function in the FROM clause by using the TABLE() function.
SQL> select * from table( get_dept_emps (10) )
2 /
ENAME SAL MGR
------------------------------ ---------- ---------------------
BOEHMER 2450 SCHNEIDER
SCHNEIDER 5000
KISHORE 1300 BOEHMER
SQL>
This can be treated like any other table, for instance, by joining to it:
SQL> select t.*
2 , e.empno
3 from
4 table( get_dept_emps (10) ) t
5 join emp e
6 on e.ename = t.ename
7 /
ENAME SAL MGR EMPNO
---------- ---------- ---------- ----------
BOEHMER 2450 SCHNEIDER 7782
SCHNEIDER 5000 7839
KISHORE 1300 BOEHMER 7934
SQL>
Since 12c, Oracle supports both APPLY and LATERAL natively: https://docs.oracle.com/database/121/NEWFT/chapter12101.htm#FEATURENO10330

Resources