To display data using cursors in pl/sql - oracle

I have written this code to 'display the department names from department table using cursors'
declare
v_dname department.department_name%type;
cursor dept_cursor is select department_name from department;
begin
open dept_cursor;
loop
fetch dept_cursor into v_dname;
exit
when dept_cursor%notfound;
dbms_output.put_line('Department names are :' || v_dname);
end loop;
close dept_cursor;
end;
/
This code runs fine and shows shows 'the procedure is created', but the output values are not being displayed. I tried running the 'dbms_output.put_line' statement alone, it worked. I don't know what else to check. Please help, and thanks in advance!

Did you SET SERVEROUTPUT ON? If not literally, then enable it in your GUI. Because, code itself looks (and works) OK. I don't have your table, but Scott's DEPT is OK for testing as well.
Your code, unmodified (apart from DEPARTMENT being changed to DEPT):
SQL> set serveroutput on --> did you do this?
SQL>
SQL> declare
2 v_dname dept.dname%type;
3
4 cursor dept_cursor is select dname from dept;
5 begin
6 open dept_cursor;
7
8 loop
9 fetch dept_cursor into v_dname;
10
11 exit when dept_cursor%notfound;
12 dbms_output.put_line ('Department names are :' || v_dname);
13 end loop;
14
15 close dept_cursor;
16 end;
17 /
Department names are :ACCOUNTING
Department names are :RESEARCH
Department names are :SALES
Department names are :OPERATIONS
PL/SQL procedure successfully completed.
SQL>
If it still doesn't work, check whether table contains any rows.
One more thing: you said
This code runs fine and shows shows 'the procedure is created'
This isn't actually an Oracle message so I'm not sure whether I'm interpreting it correctly, but: if you actually created a stored procedure (what you posted is an anonymous PL/SQL block), message says Procedure created. It, furthermore, means that you have to actually execute it to get some result.
For example, if procedure's name was p_show_dept, you'd
SQL> set serveroutput on
SQL>
SQL> begin --> executing the procedure
2 p_show_dept;
3 end;
4 /
Department names are :ACCOUNTING
Department names are :RESEARCH
Department names are :SALES
Department names are :OPERATIONS
PL/SQL procedure successfully completed.
SQL>

Related

PL/SQL procedure with cursor and rowtype

I am working in a games data base. I want to create a procedure which shows the games created between two dates.
I am using a cursor and a rowtype like this:
CREATE OR REPLACE procedure p_games(v_date1 games.date%type, v_date2 games.date%type)
AS
v_games games%rowtype;
CURSOR checkGames IS
SELECT * INTO v_games
FROM games
WHERE date BETWEEN v_date1 AND v_date2;
BEGIN
FOR register IN checkGames LOOP
dbms_output.put_line(register.v_games);
END LOOP;
END;
/
but when I run it the error is
PLS-00302: the component 'V_GAMES' must be declared.
Should I declare it in any other way?
Not exactly like that.
you don't have to declare cursor variable as you're using a cursor FOR loop
you don't select INTO while declaring a cursor; you would FETCH into if you used a different approach (see example below)
Sample table:
SQL> create table games
2 (id number,
3 c_date date
4 );
Table created.
SQL> insert into games (id, c_date) values (1, date '2022-04-25');
1 row created.
Your procedure, slightly modified:
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 BEGIN
9 FOR register IN checkGames LOOP
10 dbms_output.put_line(register.id);
11 END LOOP;
12 END;
13 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>
A different approach; as you can notice, a cursor FOR loop is way simpler as Oracle does most of the dirty job for you (opening the cursor, fetching from it, taking care about exiting the loop, closing the cursor):
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 v_games checkGames%rowtype;
9 BEGIN
10 open checkGames;
11 loop
12 fetch checkGames into v_games;
13 exit when checkGames%notfound;
14
15 dbms_output.put_line(v_games.id);
16 END LOOP;
17 close checkGames;
18 END;
19 /
Procedure created.
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>

Oracle DBMS_ALERT in Oracle 12c

I have a table (my_tab) that contains a STATUS column against a specific ID in this same table.
I need a means of being alerted via a DBMS_ALERT process of when the STATUS column changes value.
I was looking at using a trigger to kick off the ALERT, i.e.:
create or replace trigger my_tab_upd after update of status on my_tab for each row
begin
dbms_alert.signal('mystatusalert', 'changed from '||:old.status||' to '||:new.status||'.');
end;
/
With this, how do I now get alerted/notified that this STATUS change has occurred within a PL/SQL procedure to now go off and perform another operation based on this STATUS change?
Further to the above, with my application setup, there will be multiple users. Based on this, how can I target the alert for specific users/sessions so that the correct user gets their alert only and not someone else's.
I am looking at checking the alert from a web based application (Oracle APEX), so don't want to lock the front-end up so any recommendations on this would be good.
An example would be great.
I'd send an e-mail to myself. For example:
create or replace trigger my_tab_upd
after update of status on my_tab
for each row
begin
utl_mail.send (sender => 'me#company.com',
recipients => 'me#company.com',
subject => 'MY_TAB status changed',
message => 'old = ' || :old.status ||', new = ' || :new.status
);
end;
DBMS_ALERT example: in Scott's schema, I want to notify my stored procedure that something has changed in the EMP table and then do something (I'll just display the message).
First, create a triggger; alert name is alert_emp and will be used later in the stored procedure:
SQL> create or replace trigger trg_au_emp
2 after update on emp
3 for each row
4 begin
5 dbms_alert.signal
6 ('alert_emp', 'Salary changed for ' || :new.ename ||
7 ' from ' || :old.sal ||
8 ' to ' || :new.sal);
9 end;
10 /
Trigger created.
The procedure:
SQL> create or replace procedure p_test is
2 l_msg varchar2(200);
3 l_status number;
4 begin
5 dbms_alert.register ('alert_emp');
6 dbms_alert.waitone ('alert_emp', l_msg, l_status);
7 dbms_output.put_line(l_msg ||': '|| l_status);
8 end;
9 /
Procedure created.
Now, execute the procedure:
SQL> exec p_test;
Here, it is just waiting for something to happen in the EMP table. In another session I'm updating the table. Commit is obligatory; otherwise, nothing happens. p_test will still be waiting.
update emp set sal = 1000 where empno = 7369;
commit;
In the first session, once commit is being executed, screen shows this:
PL/SQL procedure successfully completed.
Salary changed for SMITH from 800 to 1000: 0
PL/SQL procedure successfully completed.
SQL>

PL/SQL Stored Procedure - return records from a table

I am very new to doing SQL work. I was wondering how I return a select statement in a stored procedure in PL/SQL.
My understanding so far (that is little) is that I should put the return of the data in a table and assign the data within the table to a reference cursor. With that loaded then LOOP through the REF Cursor and present the data back?
Actually converting that into code for a stored procedure has lost me completely with little examples to see with my use case. Any help is appreciated.
Many thanks in advance :)
Here's one example: procedure has only one - OUT - parameter, which is a refcursor:
SQL> create or replace procedure p_test (par_rc out sys_refcursor)
2 is
3 begin
4 open par_rc for select deptno, dname, loc from dept;
5 end;
6 /
Procedure created.
In order to call such a procedure, you need to store the result into something. In order to do that, I'll declare a variable (in SQL*Plus, which is a tool I use for this example) and call the procedure using begin-end block, providing the variable name as its parameter:
SQL> var l_rc refcursor;
SQL>
SQL> begin
2 p_test (:l_rc);
3 end;
4 /
PL/SQL procedure successfully completed.
Print the result:
SQL> print l_rc
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
There might be other options, which depend on what you really are doing.
Usually a stored procedure is used to perform manipulations of data in the database, and functions are used to return values or data. If you're just trying to use a select statement within a stored procedure, then you would want to use a cursor, which is something you declare like any other variable at the beginning of the procedure, and then open either implicitly or explicitly within the procedure code.
Example of an implicit cursor:
declare
cursor sample_cur is --this can be your select statement
select sysdate as today from dual;
begin
for rec in sample_cur loop
-- step by step for each record you return in your cursor
dbms_output.put_line(rec.today);
end loop;
end;

Using cursor with where in condition

I am passing arguments `EBN,BGE' into a procedure , then I am passing this argument to a cursor.
create or replace procedure TEXT_MD (AS_IDS VARCHAR2)
is
CURSOR C_A (AS_ID VARCHAR2) IS
SELECT
name
FROM S_US
WHERE US_ID IN (AS_ID);
BEGIN
FOR A IN C_A (AS_IDS) LOOP
DBMS_OUTPUT.PUT_LINE('I got here: '||AS_IDS);
end loop;
END;
But while debuging the count of the cursor is still null
So my question , why the cursor not returning values with in condition
You are passing a string parameter, so it will be used as a string, not as a list of strings; so, your cursor will be something like
SELECT name
FROM S_US
WHERE US_ID IN ('EBN,BGE')
This will, of course, not do what you need.
You may need to change your procedure and the way to pass parameters; if you want to keep a string parameter , one way could be the following:
setup:
SQL> CREATE TABLE S_US
2 (
3 US_ID,
4 NAME
5 ) AS
6 SELECT 'EBN', 'EBN name' FROM DUAL
7 UNION ALL
8 SELECT 'BGE', 'BGE name' FROM DUAL;
Table created.
procedure:
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_2(AS_IDS VARCHAR2) IS
2 vSQL varchar2(1000);
3 c sys_refcursor;
4 vName varchar2(16);
5 BEGIN
6 vSQL := 'SELECT name
7 FROM S_US
8 WHERE US_ID IN (' || AS_IDS || ')';
9 open c for vSQL;
10 loop
11 fetch c into vName;
12 if c%NOTFOUND then
13 exit;
14 end if;
15 DBMS_OUTPUT.PUT_LINE(vName);
16 END LOOP;
17 END;
18 /
Procedure created.
You need to call it with a string already formatted to be a parameter list for IN:
SQL> EXEC TEXT_MD_2('''EBN'',''BGE''');
EBN name
BGE name
PL/SQL procedure successfully completed.
This is only an example of a possible way, and not the way I would do this.
Among the reasons to avoud this kind of approach, consider what Justin Cave says:
"that would be a security risk due to SQL injection and would have a potentially significant performance penalty due to constant hard parsing".
I believe you should better check how to pass a list of values to your procedure, rather then using a string to represent a list of strings.
Here is a possible way to do the same thing with a collection:
SQL> CREATE OR REPLACE TYPE tabVarchar2 AS TABLE OF VARCHAR2(16)
2 /
Type created.
SQL>
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_3(AS_IDS tabVarchar2) IS
2 vSQL VARCHAR2(1000);
3 c SYS_REFCURSOR;
4 vName VARCHAR2(16);
5 BEGIN
6 FOR i IN (SELECT name
7 FROM S_US INNER JOIN TABLE(AS_IDS) tab ON (tab.COLUMN_VALUE = US_ID))
8 LOOP
9 DBMS_OUTPUT.PUT_LINE(i.name);
10 END LOOP;
11 END;
12 /
Procedure created.
SQL>
SQL> DECLARE
2 vList tabVarchar2 := NEW tabVarchar2();
3 BEGIN
4 vList.EXTEND(2);
5 vList(1) := 'BGE';
6 vList(2) := 'EBN';
7 TEXT_MD_3(vList);
8 END;
9 /
BGE name
EBN name
PL/SQL procedure successfully completed.
SQL>
Again, you can define collections in different ways, within a stored procedure or not, indexed or not, and so on; this is only one of the possible ways, not necessarily the best, depending on your environment, needs.

SQL Developer Oracle, how to call procedure? [duplicate]

This question already has answers here:
Calling a stored PROCEDURE in Toad
(2 answers)
Closed 7 years ago.
I have delcared function like this:
CREATE or replace PROCEDURE proc
(
P_ID IN INTEGER,
NAME OUT CHAR,
SURNAME OUT CHAR,
TOTAL OUT CHAR
)
AS
BEGIN
SELECT NAME, SURNAME, sum(TOTAL) AS TOT
INTO NAME,SURNAME,TOTAL
FROM STATISTICS, PLAYERS, PERSON
WHERE STATISTICS.SID=P_ID AND PERSON.ID=PLAYERS.SID AND
STATISTICS.PLAYERS_SID=PLAYERS.SID
GROUP BY NAME,SURNAME;
END;
Select statement works corectly, but how to call this procedure in Oracle?
I tried something like
EXEC proc(4);
AND
DECLARE
NAME OUT CHAR,
SURNAME OUT CHAR,
TOTAL OUT CHAR
BEGIN
P_ID := 12 ;
proc (
P_ID => P_ID,
NAME => NAME,
SURNAME => SURNAME,
TOTAL => TOTAL
);
END;
but without any success.
EXEC proc(4);
EXECUTE is a SQL*Plus command.
You have following options:
EXECUTE in SQL*Plus
Call it in an anonymous PL/SQL block.
Run in SQL Developer client tool
Let's see all the three ways:
In SQL*Plus:
SQL> variable v_ename varchar2(20);
SQL> exec get_emp(7788, :v_ename);
PL/SQL procedure successfully completed.
SQL> print v_ename;
V_ENAME
--------------------------------
SCOTT
In an anonymous PL/SQL block:
SQL> CREATE OR REPLACE PROCEDURE get_emp(
2 i_empno IN emp.empno%TYPE,
3 o_ename OUT emp.ename%TYPE)
4 AS
5 BEGIN
6 SELECT ename INTO o_ename FROM emp WHERE empno = i_empno;
7 END;
8 /
Procedure created.
SQL> SET serveroutput ON
SQL> DECLARE
2 v_ename VARCHAR2(20);
3 BEGIN
4 get_emp(7788, v_ename);
5 dbms_output.put_line('Employee name is '||v_ename);
6 END;
7 /
Employee name is SCOTT
PL/SQL procedure successfully completed.
In SQL Developer client tool:
Go to connections on the left pane.
Expand the Procedures.
Right click on the procedure and select "Run".
It will open a new window, provide the Input value and click OK.
The output will be shown in Output Log at the bottom as "Output Variables".

Resources