I have a function that returns a refcursor for a given query.
I call the function like this. Although this is a simple query my actual query is more dynamic with other parameters for where clause.
select myfunction('select * from employees') as f from dual;
and it gives me the output
F
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DAT JOB_ID SALARY COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
----------- -------------------- ------------------------- ------------------------- -------------------- -------- ---------- ---------- -------------- ---------- -------------
198 Donald OConnell DOCONNEL 650.507.9833 20070621 SH_CLERK 2600 124 50
199 Douglas Grant DGRANT 650.507.9844 20080113 SH_CLERK 2600 124 50
200 Jennifer Whalen JWHALEN 515.123.4444 20030917 AD_ASST 4400 101 10
201 Michael Hartstein MHARTSTE 515.123.5555 20040217 MK_MAN 13000 100 20
202 Pat Fay PFAY 603.123.6666 20050817 MK_REP 6000 201 20
203 Susan Mavris SMAVRIS 515.123.7777 20020607 HR_REP 6500 101 40
204 Hermann Baer HBAER 515.123.8888 20020607 PR_REP 10000 101 70
I don't want these lines at the top and only want the cursor's output.
CURSOR STATEMENT : 1
How do I stop sqlplus or SQL developer from displaying it? what commands should I use?
That looks like a function that returns a ref cursor; is it?
SQL> create or replace function f_my (par_select in varchar2)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for par_select;
7 return l_rc;
8 end;
9 /
Function created.
Here's what you currently have:
SQL> select f_my('select * From dept') as f from dual;
F
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
So, how to get rid of those CURSOR STATEMENT : 1 lines? It is SQLPlus that's displaying them (some other tool might not do it). But, as you use SQLPlus, here's how:
SQL> var v_rc refcursor
SQL> exec :v_rc := f_my('select * from dept')
PL/SQL procedure successfully completed.
SQL> print v_rc
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Thanks to Littlefoot's answer. I am able to extract only the rows from the table
using the following lines in my script.
SET FEEDBACK OFF
SET PAGES 0
VARIABLE c REFCURSOR
spool /mypath/somefile.csv
EXEC :c := myfunction('select * from employees')
PRINT c
Related
i am beginner so i am confusing about what should i write in return type.
and how to write function to select all records from particular ID
2 shadik 27 25-05-14 4000 pakistan
1 AKSHAY 28 30-04-20 2000 INDIA
3 GAURANG 25 06-05-20 4000 USA
4 NIRAV 23 16-11-14 1000 CANADA
5 VEER 29 26-12-19 5000 DUBAI
A simple option is to return refcursor; you don't have to declare any type for that.
Here's an example based on Scott's sample schema. Function returns all rows from the EMP table whose DEPTNO column matches value passed as function's IN parameter.
SQL> create or replace function f_test (par_deptno in number)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for select * from emp
7 where deptno = par_deptno;
8 return rc;
9 end;
10 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
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
SQL>
Though this is a bit more custom you can return rows from a function using a type.
create or replace type myRecordType
as object
( x int,
y date,
z varchar2(25)
)
create or replace type myTableType
as table of myRecordType;
/
create or replace function my_function return myTableType
as
some_data myTableType := myTabletype();
begin
for i in 1 .. 10 loop
some_data.extend;
some_data(i) :=
myRecordType( i, sysdate+i, 'record ' || i );
end loop;
return some_data;
end;
/
select * from TABLE ( cast( my_function() as mytableType ) )
/
X Y Z
1 09-FEB-22 record 1
2 10-FEB-22 record 2
3 11-FEB-22 record 3
4 12-FEB-22 record 4
5 13-FEB-22 record 5
6 14-FEB-22 record 6
7 15-FEB-22 record 7
8 16-FEB-22 record 8
9 17-FEB-22 record 9
10 18-FEB-22 record 10
I have a table T1 which 3 columns with 100 records. All columns and rows were filled but the first column named ID values are empty. So, I wanted to fill the ID column with numbering order(1,2..100) for 100 rows by using PL/SQL Program. I have tried with rownum and with a sequence which is working fine. I want to try with pl/SQL block. I have also tried to write pl/SQL block, however, not getting the desired result.
declare
count1 number;
begin
SELECT COUNT(1) INTO COUNT1 FROM T1;
FOR I IN 1..COUNT1
loop
UPDATE T1 SET ID =I;
end loop;
end;
SQL should be the way to do it; but OK, if you're learning PL/SQL, then this might be one option:
Sample table (ID column should be populated):
SQL> create table test (id number, name varchar2(10));
Table created.
SQL> insert into test (name) select ename from emp;
14 rows created.
SQL> select * From test;
ID NAME
---------- ----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
14 rows selected.
Anonymous PL/SQL block:
SQL> declare
2 i number := 1;
3 begin
4 for cur_r in (select rowid rid from test) loop
5 update test set id = i where rowid = cur_r.rid;
6 i := i + 1;
7 end loop;
8 end;
9 /
PL/SQL procedure successfully completed.
Result:
SQL> select * From test;
ID NAME
---------- ----------
1 SMITH
2 ALLEN
3 WARD
4 JONES
5 MARTIN
6 BLAKE
7 CLARK
8 SCOTT
9 KING
10 TURNER
11 ADAMS
12 JAMES
13 FORD
14 MILLER
14 rows selected.
SQL>
Loops are slow, they process the table row-by-row. Yet another option (you didn't mention and - perhaps - didn't try - is merge.
SQL> update test set id = null;
14 rows updated.
SQL> begin
2 merge into test a
3 using (select b.rowid,
4 row_number() over (order by null) rn
5 from test b
6 ) x
7 on (a.rowid = x.rowid)
8 when matched then update set
9 a.id = x.rn;
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> select * from test;
ID NAME
---------- ----------
1 SMITH
2 ALLEN
3 WARD
4 JONES
5 MARTIN
6 BLAKE
7 CLARK
8 SCOTT
9 KING
10 TURNER
11 ADAMS
12 JAMES
13 FORD
14 MILLER
14 rows selected.
SQL>
My journey is progressing and I'm learning rapidly. I can't get enough of this stuff... alas I am at a dead end here and need some help.
I am running Visual Studio, am connected to a database (that's filled with dummy data). I am able to run queries on it as expected. However, I'm learning about Procedures right now and I'm coming up with a problem.
I am trying to simply run a query that will select all from table.
CREATE PROCEDURE nearthetop()
BEGIN
SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS))
END;
When I run this inside Visual Studio I get an error:
EXECUTE FAIL:
CREATE PROCEDURE twofromtop() BEGIN SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS)) END
Message :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 3
When I remove BEGIN and END; from the procedure it creates it fine.
Why is that?
And then, once created, how do I "call" that procedure so as I can see the returned results?
In Oracle, when PL/SQL code uses a SELECT statement, you have to select into something - a local variable, a collection, whatever - or use a cursor loop and deal with cursor variable.
You're selecting row(s) whose volume value is the 2nd largest. Code you wrote works, but it fetches from the same table 3 times which isn't optimal. For example (based on Scott's sample schema), fetching the 2nd largest salary:
SQL> select ename, sal from emp order by sal desc;
ENAME SAL
---------- ----------
KING 5000 --> largest
FORD 3000 --> Ford and Scott both share
SCOTT 3000 --> the 2nd largest salary
JONES 2975
BLAKE 2850
CLARK 2450
<snip>
Your query returns correct result:
SQL> select *
2 from emp
3 where sal = (select max(sal) from emp
4 where sal not in (select max(sal) from emp)
5 );
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7902 FORD ANALYST 7566 03.12.81 3000 20
Consider doing it differently:
SQL> select *
2 from (select e.*,
3 rank() over (order by sal desc) rnk
4 from emp e
5 )
6 where rnk = 2;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
Now, back to your procedure. A simple option which doesn't require much effort is to use a cursor FOR loop. Procedure's IN parameter says which largest salary you want:
SQL> create or replace procedure nearthetop (par_n in number) as
2 begin
3 for cur_r in (select *
4 from (select e.*,
5 rank() over (order by sal desc) rnk
6 from emp e
7 )
8 where rnk = par_n
9 )
10 loop
11 dbms_output.put_line(cur_r.ename ||': '|| cur_r.sal);
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> begin
2 nearthetop(2); --> give me the 2nd largest salary
3 end;
4 /
SCOTT: 3000
FORD: 3000
PL/SQL procedure successfully completed.
SQL> begin
2 nearthetop(5); --> give me the 5th largest salary
3 end;
4 /
BLAKE: 2850
PL/SQL procedure successfully completed.
SQL>
I'm just displaying those values on the screen; you never said what you'd want to do with them.
If you'd like to return the result to the caller, a better option is to use a function instead of a procedure. In this case, it would be a ref cursor it returns.
SQL> create or replace function f_nearthetop (par_n in number)
2 return sys_refcursor
3 as
4 rc sys_refcursor;
5 begin
6 open rc for select *
7 from (select e.*,
8 rank() over (order by sal desc) rnk
9 from emp e
10 )
11 where rnk = par_n;
12 return rc;
13 end;
14 /
Function created.
Testing:
SQL> var l_rc refcursor
SQL>
SQL> exec :l_rc := f_nearthetop(2);
PL/SQL procedure successfully completed.
SQL> print :l_rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
So, yes - there are various options. Which one you'll actually use depends on what you want to do.
(As of Visual Studio: I can't help about it, I don't use it.)
Need help understanding how to use a cursor variable in a procedure. And use the anonymous block to call the procedure 6 times to run a set of queries. Trying to figure this out have been giving me a headache, thank you in advance for any help.
CREATE OR REPLACE PROCEDURE City_Jail_SP
(lv_query IN NUMBER,
lv_out out VARCHAR2)
AS
cursor qry_lvl IS
BEGIN
OPEN qry_lvl;
LOOP
FETCH INTO
IF chioce = 1 THEN SELECT AVG(COUNT(*))
FROM crime_officers
GROUP BY officer_id;
ELSIF chioce = 2 THEN SELECT MIN(Fine_amount)
FROM Crime_charges;
ELSIF chioce = 3 THEN COLUMN (hearing_date-date_charged) HEADING DAYS
SELECT crime_id, Classification, date_charged, hearing_date,
( hearing_date-date_charged)
FROM crimes
WHERE hearing_date-date_charged >14;
ELSIF choice = 4 THEN select cl.criminal_id, cl.last, cl.first, cc.Crime_code, cc.Fine_amount
FROM criminals cl
JOIN crimes cr
ON cr.criminal_id = cl.criminal_id
JOIN crime_charges cc
ON cc.crime_id = cr.crime_id;
ELSIF chioce = 5 THEN SELECT LAST, FIRST
FROM officers JOIN crime_officers USING (officer_id)
JOIN crimes USING (crime_id)
GROUP BY (LAST, FIRST)
HAVING COUNT(crime_id)>(SELECT AVG(COUNT(crime_id))
FROM crimes JOIN crime_officers using (crime_id)
GROUP BY officer_id);
ELSIF choice = 6 THEN SELECT DISTINCT FIRST, LAST
FROM criminals JOIN crimes USING (criminal_id)
JOIN crime_charges USING (crime_id)
GROUP BY (FIRST, LAST)
HAVING COUNT(Criminal_ID)<(SELECT AVG(COUNT(Criminal_ID))
FROM crimes JOIN criminals USING (Criminal_ID)
GROUP BY Criminal_ID)
ORDER BY FIRST, LAST;
END IF;
close qry_lvl;
END;
/
It looks as if code you wrote should be shift-deleted so that you could start over.
you started to declare a cursor, but never finished it
you didn't declare any cursor variable to store cursor's values into it
anyway, you'd rather switch to a cursor FOR loop as it is simpler to use
you're fetching into ... what?
there's no end loop
you declared IN and OUT parameters, but never used any of them
there's bunch of select statements in IF, almost all of them returning different data set. Also, there's no into clause in any of them. PL/SQL requires it, so you'll have to declare quite a few local variables
typos, typos, typos ... is it chioce or choice?
what is that column ... heading days thing supposed to do? Sounds like something related to SQL*Plus
Maybe you'd actually want to use refcursors. Here's a simple example based on Scott's schema. Depending on department number, I'm returning different data set.
Sample data:
SQL> SELECT * FROM dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Procedure (kind of simulates what you tried to do):
SQL> CREATE OR REPLACE PROCEDURE p_test (par_deptno IN NUMBER,
2 par_rc OUT SYS_REFCURSOR)
3 IS
4 l_dname dept.dname%TYPE;
5 BEGIN
6 SELECT dname
7 INTO l_dname
8 FROM dept
9 WHERE deptno = par_deptno;
10
11 IF l_dname = 'ACCOUNTING'
12 THEN
13 OPEN par_rc FOR SELECT d.dname, e.ename, e.job
14 FROM dept d
15 JOIN emp e
16 ON e.deptno = d.deptno
17 AND d.deptno = par_deptno;
18 ELSIF l_dname = 'SALES'
19 THEN
20 OPEN par_rc FOR SELECT e.ename, e.job, e.sal
21 FROM emp e
22 WHERE e.job = 'SALESMAN';
23 END IF;
24 END;
25 /
Procedure created.
Testing:
SQL> var l_rc refcursor
SQL> exec p_test(10, :l_rc);
PL/SQL procedure successfully completed.
SQL> print :l_rc
DNAME ENAME JOB
-------------- ---------- ---------
ACCOUNTING CLARK MANAGER
ACCOUNTING KING PRESIDENT
ACCOUNTING MILLER CLERK
SQL> exec p_test(30, :l_rc);
PL/SQL procedure successfully completed.
SQL> print :l_rc
ENAME JOB SAL
---------- --------- ----------
ALLEN SALESMAN 1600
WARD SALESMAN 1250
MARTIN SALESMAN 1250
TURNER SALESMAN 1500
SQL>
Please, read some basic PL/SQL documentation, you can't start coding and making up your own syntax. This is Oracle 12c PL/SQL Language Reference. It will take you some time to read it, but - at least - you'll know what you're doing (I hope so).
I'm stuck on getting my stored procedure to work because I made a VARRAY data type to store all information about allergies, my goal is to input an integer primary key and the result will return the information respected to that primary key.
CREATE OR REPLACE PROCEDURE showAllergies
(
P_PID IN NUMBER,
P_f_name OUT VARCHAR2,
P_l_name OUT VARCHAR2,
P_allergies OUT allergy_ty
)
AS
BEGIN
SELECT p.name.f_name,
p.name.l_name,
al.allergies
INTO P_f_name,
P_l_name,
P_allergies
FROM PATIENT_obj_table p
INNER JOIN PATIENT_allergies al ON p.PID = al.PID
WHERE
al.PID = P_PID;
END showAllergies;
--Must do this in order to execute multiple output parameters
set serveroutput ON;
DECLARE
fname VARCHAR2(25);
lname VARCHAR2(25);
allergies allergy_ty;
total NUMBER(2,0);
begin
--select value(a) INTO allergies FROM PATIENT_allergies WHERE a.PID = 100;
total := allergies.limit;
showAllergies(101, fname, lname, allergies);
DBMS_OUTPUT.PUT_LINE('firstName: '||fname||', lastName: '||lname||' Allergies ');
FOR i in 1 .. total
LOOP DBMS_OUTPUT.PUT_LINE(allergies(i));
end loop;
end;
END;
CREATE OR REPLACE TYPE allergy_ty AS VARRAY(10)
OF VARCHAR2(30);
I don't know how get VARRAY type to print using stored procedure do ya'll lend me some help? -Thanks
You didn't post how tables you use look like, so I'm going to guess. Moreover, I'll use Scott's EMP and DEPT tables to simulate what you have; I'll display department (which is your "patient") and employees who work in that department (which is "allergies" patients suffer from).
SQL> -- this table simulates your PATIENT table
SQL> select * From dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> -- this table simulates your ALLERGIES table
SQL> select deptno, ename from emp;
DEPTNO ENAME
---------- ----------
20 SMITH
30 ALLEN
30 WARD
20 JONES
30 MARTIN
30 BLAKE
10 CLARK
20 SCOTT
10 KING
30 TURNER
20 ADAMS
30 JAMES
20 FORD
10 MILLER
14 rows selected.
SQL>
This is more or less code you wrote; note the way I selected values into p_allergies parameter - not with a join, but a separate query:
SQL> CREATE OR REPLACE TYPE allergy_ty AS VARRAY(10) OF VARCHAR2(30);
2 /
Type created.
SQL> create or replace procedure showallergies
2 (p_pid in number,
3 p_f_name out varchar2,
4 p_l_name out varchar2,
5 p_allergies out allergy_ty
6 )
7 is
8 begin
9 select d.dname, d.loc
10 into p_f_name, p_l_name
11 from dept d
12 where d.deptno = p_pid;
13
14 select e.ename
15 bulk collect into p_allergies
16 from emp e
17 where e.deptno = p_pid;
18 end;
19 /
Procedure created.
SQL>
Let's see how it works:
SQL> set serveroutput on;
SQL> declare
2 fname varchar2(20);
3 lname varchar2(20);
4 allergies allergy_ty;
5 begin
6 showallergies(10, fname, lname, allergies);
7 dbms_output.put_line(fname ||', '|| lname);
8
9 for i in 1 .. allergies.count loop
10 dbms_output.put_line(allergies(i));
11 end loop;
12 end;
13 /
ACCOUNTING, NEW YORK
CLARK
KING
MILLER
PL/SQL procedure successfully completed.
SQL>
Works OK. Now, try to do the same with your tables.